/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package org.autoplot.state; import org.autoplot.dom.DomUtil; import org.autoplot.dom.BindingModel; import org.autoplot.dom.DomNode; import org.autoplot.dom.Connector; import java.awt.Color; import java.beans.IntrospectionException; import java.text.ParseException; import java.beans.BeanInfo; import java.beans.IndexedPropertyDescriptor; import java.beans.PropertyDescriptor; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.das2.beans.BeansUtil; import org.das2.datum.Datum; import org.das2.graph.AnchorPosition; import org.das2.graph.AnchorType; import org.das2.graph.BorderType; import org.das2.graph.DasColorBar; import org.das2.graph.DefaultPlotSymbol; import org.das2.graph.LegendPosition; import org.das2.graph.PlotSymbol; import org.das2.graph.PsymConnector; import org.das2.graph.SpectrogramRenderer; import org.autoplot.MouseModuleType; import org.autoplot.RenderType; import org.das2.qstream.SerializeDelegate; import org.das2.qstream.SerializeRegistry; import org.das2.qstream.XMLSerializeDelegate; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Utility class for creating a Document from a DomNode. Note that there is special * handling for: * controller -- these nodes are dropped. * class -- this is noise from java. * *Automatically -- this is a property used to set another property. * There may be other exceptional properties that are not documented here. * @author jbf */ public class SerializeUtil { private static final Logger logger = org.das2.util.LoggerManager.getLogger("autoplot.dom.vap"); static { SerializeRegistry.register( BindingModel.class, new BindingModelSerializeDelegate() ); SerializeRegistry.register( Connector.class, new ConnectorSerializeDelegate() ); SerializeRegistry.register( Datum.class, new DatumSerializeDelegate() ); //SerializeRegistry.register( Displayable.class, new EnumSerializeDelegate() ); SerializeRegistry.register( Enum.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( Color.class, new ColorSerializeDelegate() ); SerializeRegistry.register( DasColorBar.Type.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( DefaultPlotSymbol.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( PsymConnector.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( SpectrogramRenderer.RebinnerEnum.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( RenderType.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( MouseModuleType.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( PlotSymbol.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( LegendPosition.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( AnchorPosition.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( BorderType.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( AnchorType.class, new TypeSafeEnumSerializeDelegate() ); SerializeRegistry.register( Level.class, new LevelSerializeDelegate() ); } /** * Return the XML for the node. * @param document the document to which the node is added. * @param node the dom node (Application, Plot, PlotElement, etc.) * @param scheme the version of the vap that we are writing. This identifies the scheme, but also provides names for nodes. * @return the Document (XML) element for the node. */ public static Element getDomElement( Document document, DomNode node, VapScheme scheme ) { return getDomElement( document, node, scheme, true ); } /** * Return the XML for the node. * @param document the document to which the node is added. * @param node the dom node (Application, Plot, PlotElement, etc.) * @param scheme the version of the vap that we are writing. This identifies the scheme, but also provides names for nodes. * @param includeDefaults if true, include nodes which are the default setting * @return the Document (XML) element for the node. */ public static Element getDomElement( Document document, DomNode node, VapScheme scheme, boolean includeDefaults ) { try { String elementName = scheme.getName(node.getClass()); DomNode defl = node.getClass().newInstance(); Element element; element = document.createElement(elementName); BeanInfo info = BeansUtil.getBeanInfo(node.getClass()); PropertyDescriptor[] properties = info.getPropertyDescriptors(); for (PropertyDescriptor pd : properties) { String propertyName = pd.getName(); if ( propertyName.equals("class") ) continue; if (propertyName.equals("controller")) { //special node should runtime data continue; } // I made the mistake of making the connectors a proper DOM node, // without realizing this was going to affect the vap. This is // kludge to avoid saving out the node and preserving v1.07 for // the vap files. boolean connectorKludge107= false; if ( propertyName.equals("connectors") ) { connectorKludge107= true; } // setters like "setComponentAutomatically" which should probably not be in the dom node anyway. if ( propertyName.endsWith("Automatically" ) ) { continue; } logger.log(Level.FINE, "serializing property \"{0}\" of {1} id={2}", new Object[]{propertyName, elementName, node.getId()}); Method readMethod = pd.getReadMethod(); Method writeMethod = pd.getWriteMethod(); if (writeMethod == null || readMethod == null) { logger.log(Level.FINE, "skipping property \"{0}\" of {1}, failed to find read and write method.", new Object[]{propertyName, elementName}); continue; } Object value; try { value = readMethod.invoke(node, new Object[0]); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { logger.log(Level.SEVERE, ex.getMessage(), ex); continue; } if (value == null) { logger.log(Level.INFO, "skipping property {0} of {1}, value is null.", new Object[]{propertyName, elementName}); continue; } if (propertyName.equals("id") && ((String) value).length() > 0) { element.setAttribute(propertyName, (String) value); continue; } IndexedPropertyDescriptor ipd = null; if (pd instanceof IndexedPropertyDescriptor) { ipd = (IndexedPropertyDescriptor) pd; } if (value instanceof DomNode) { // special optimization, only serialize at the first reference to DCC, afterwards just use name Element propertyElement = document.createElement("property"); propertyElement.setAttribute("name", propertyName); propertyElement.setAttribute("type", "DomNode"); Element child = getDomElement(document, (DomNode) value, scheme, includeDefaults ); propertyElement.appendChild(child); element.appendChild(propertyElement); } else if (ipd != null && !connectorKludge107 && (DomNode.class.isAssignableFrom(ipd.getIndexedPropertyType()))) { // serialize each element of the array. Assumes order doesn't change Element propertyElement = document.createElement("property"); propertyElement.setAttribute("name", propertyName); String clasName = scheme.getName(ipd.getIndexedPropertyType()); propertyElement.setAttribute("class", clasName); propertyElement.setAttribute("length", String.valueOf(Array.getLength(value))); for (int j = 0; j < Array.getLength(value); j++) { Object value1 = Array.get(value, j); Element child = getDomElement(document, (DomNode) value1, scheme, includeDefaults ); propertyElement.appendChild(child); } element.appendChild(propertyElement); } else if (ipd != null) { // array of non-DomNodes, such as bindings. Element propertyElement = document.createElement("property"); propertyElement.setAttribute("name", propertyName); String clasName = scheme.getName(ipd.getIndexedPropertyType()); propertyElement.setAttribute("class", clasName); propertyElement.setAttribute("length", String.valueOf(Array.getLength(value))); for (int j = 0; j < Array.getLength(value); j++) { Object value1 = Array.get(value, j); Element child = getElementForLeafNode(document, ipd.getIndexedPropertyType(), value1, null); propertyElement.appendChild(child); } element.appendChild(propertyElement); } else { Object defltValue = DomUtil.getPropertyValue(defl, pd.getName()); if ( !value.equals(defltValue) || includeDefaults ) { Element prop = getElementForLeafNode(document, pd.getPropertyType(), value, defltValue); if (prop == null) { logger.log(Level.WARNING, "unable to serialize {0}", propertyName); //prop = getElementForLeafNode(document, pd.getPropertyType(), value, defltValue); continue; } prop.setAttribute("name", pd.getName()); element.appendChild(prop); } } } return element; } catch (IntrospectionException | IllegalArgumentException | InvocationTargetException | InstantiationException | IllegalAccessException ex) { logger.log(Level.SEVERE, ex.getMessage(), ex); throw new RuntimeException(ex); } } /** * return the Element, or null if we can't handle it * @param document * @param propClass * @param value * @param defltValue * @return */ public static Element getElementForLeafNode( Document document, Class propClass, Object value, Object defltValue ) { boolean isDef= defltValue==value || (defltValue!=null && defltValue.equals(value) ); SerializeDelegate sd= SerializeRegistry.getDelegate(propClass); if ( sd==null ) { return null; } else { Element prop = document.createElement("property"); if ( sd instanceof XMLSerializeDelegate ) { prop.appendChild( ((XMLSerializeDelegate)sd).xmlFormat(document,value) ); prop.setAttribute("type", sd.typeId(value.getClass())); } else { prop.setAttribute("type", sd.typeId(value.getClass())); prop.setAttribute("value", sd.format(value)); if ( !isDef ) { if ( defltValue==null ) { prop.setAttribute("default", "null" ); } else { prop.setAttribute("default", sd.format(defltValue) ); } } } return prop; } } /** * returns the first child that is an element. * @param e * @throws IllegalArgumentException if the element has not children that are elements. * @return */ private static Element firstChildElement( Element element ) { NodeList nl= element.getChildNodes(); for ( int i=0; i pp= new HashMap(); for (PropertyDescriptor property : properties) { pp.put(property.getName(), property); } if ( element.hasAttribute("id") ) { node.setId( element.getAttribute("id") ); } NodeList kids= element.getChildNodes(); for ( int i=0; i0 ) { clasName= e.getAttribute("class"); Class c= scheme.getClass(clasName); int n= Integer.parseInt(e.getAttribute("length")); Object arr= Array.newInstance( c,n ); boolean connectorKludge107= c==Connector.class; if ( !connectorKludge107 && DomNode.class.isAssignableFrom(c) ) { NodeList arraykids= e.getChildNodes(); int ik=0; for ( int j=0; j0 ) { logger.log( Level.WARNING, "Unresolved: {0}", unres); } return node; } catch (IntrospectionException | IllegalArgumentException | InstantiationException | IllegalAccessException ex) { logger.log(Level.SEVERE, ex.getMessage(), ex); throw new RuntimeException(ex); } } }