[MOBY-guts] biomoby commit

Martin Senger senger at dev.open-bio.org
Thu Feb 28 05:21:48 UTC 2008


senger
Thu Feb 28 00:21:48 EST 2008
Update of /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser
In directory dev.open-bio.org:/tmp/cvs-serv20124/src/main/org/biomoby/shared/parser

Modified Files:
	MobyPackage.java MobyParser.java 
Log Message:
* the jMoby XML message parser can accept now more specialized types of members than the ones its parent was registered with
* added junit tests for the parser

moby-live/Java/src/main/org/biomoby/shared/parser MobyPackage.java,1.7,1.8 MobyParser.java,1.7,1.8
===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser/MobyPackage.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser/MobyPackage.java	2006/06/28 16:28:30	1.7
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser/MobyPackage.java	2008/02/28 05:21:48	1.8
@@ -19,6 +19,7 @@
 import org.jdom.output.XMLOutputter;
 import org.jdom.output.Format;
 
+import java.util.Map;
 import java.util.Vector;
 import java.util.Enumeration;
 import java.io.StringReader;
@@ -78,7 +79,7 @@
      *************************************************************************/
     public static MobyPackage createFromXML (Object xmlData)
 	throws MobyException {
-	return createFromXML (xmlData, null);
+	return createFromXML (xmlData, null, null);
     }
 
     /**************************************************************************
@@ -92,8 +93,43 @@
     public static MobyPackage createFromXML (Object xmlData,
 					     String lowestKnownDataType)
 	throws MobyException {
+	return createFromXML (xmlData, lowestKnownDataType, null);
+    }
+
+    /**************************************************************************
+     * Constructing a MobyPackage object from XML. The input XML can
+     * be given as a String, byte[], or a File. <p>
+     *
+     * Additionally, it passes to the XML parser the
+     * 'lowestKnownDataType' as a falback object (the role of a
+     * fallback object is explained in {@link MobyParser}.
+     *************************************************************************/
+    public static MobyPackage createFromXML (Object xmlData,
+					     Map<String,String> lowestKnownDataTypes)
+	throws MobyException {
+	return createFromXML (xmlData, null, lowestKnownDataTypes);
+    }
+
+    /**************************************************************************
+     * Constructing a MobyPackage object from XML. The input XML can
+     * be given as a String, byte[], or a File. <p>
+     *
+     * Additionally, it passes to the XML parser the
+     * 'lowestKnownDataType' as a falback object (the role of a
+     * fallback object is explained in {@link MobyParser}.
+     *************************************************************************/
+    protected static MobyPackage createFromXML (Object xmlData,
+						String lowestKnownDataType,
+						Map<String,String> lowestKnownDataTypes)
+	throws MobyException {
 	
-	MobyParser parser = new MobyParser (lowestKnownDataType);
+	MobyParser parser = null;
+	if (lowestKnownDataTypes != null && lowestKnownDataTypes.size() > 0) {
+	    parser = new MobyParser (lowestKnownDataTypes);
+	} else {
+	    parser = new MobyParser (lowestKnownDataType);
+	}
+
 	if (xmlData instanceof byte[]) {
 	    return parser.parse ( new ByteArrayInputStream ((byte[])xmlData) );
 	} else if (xmlData instanceof File) {

===================================================================
RCS file: /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser/MobyParser.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser/MobyParser.java	2006/02/14 15:35:26	1.7
+++ /home/repository/moby/moby-live/Java/src/main/org/biomoby/shared/parser/MobyParser.java	2008/02/28 05:21:48	1.8
@@ -22,6 +22,8 @@
 import org.tulsoft.tools.xml.XMLUtils2;
 import org.tulsoft.tools.xml.XMLErrorHandler;
 
+import org.apache.commons.lang.StringUtils;
+
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 import org.xml.sax.Locator;
@@ -32,6 +34,9 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Stack;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
 import java.util.HashSet;
 import java.io.InputStream;
 import java.io.Reader;
@@ -65,7 +70,7 @@
  *
  * Because also skeletons for services can be generated, it is easy to
  * ensure that a service knows its "the most specialized" data type it
- * can still served, and that it passes it to the parser constructor. <p>
+ * can still serve, and that it passes it to the parser constructor. <p>
  *
  * If the parser finds an unknown object/tag but no substitute was
  * passed in the parser constructor, it prints a warning and ignores
@@ -92,13 +97,13 @@
  * the Biomoby registry. An example, I know about at the time of
  * writing this, is a service <tt>MIPSBlastXMLClickableImage</tt> that
  * returns object of type <tt>NCBI_Blast_XML_Gif_Map</tt> which has a
- * member object of type <tt>b64_encoded_gig</tt>. This member was
+ * member object of type <tt>b64_encoded_gif</tt>. This member was
  * registered with the article name "hitGraph" but the service returns
  * an article name "image". Parser spots it and stops parsing (because
  * it does not know where to put this member object). However, if you
  * insist that you still want to get such XML parsed, even without one
  * part, you can set Java property "biomoby.parser.forgiving" to true,
- * and run ti again. <p>
+ * and run it again. <p>
  *
  * You can test parser by using a simple <tt>TestingMobyParser</tt>
  * client. This is how to invoke it and how to get its help:
@@ -152,6 +157,9 @@
  * processed by this parser are stored as constants in class {@link
  * MobyTags}. <p>
  *
+ * The parser is not thread-safe. Make a new instance for each parsed
+ * input. <p>
+ *
  * @author <A HREF="mailto:martin.senger at gmail.com">Martin Senger</A>
  * @version $Id$
  */
@@ -172,6 +180,7 @@
     Locator locator;
     XMLReader parser = null;
     String lowestKnownDataType = null;
+    Map<String,String> lowestKnownDataTypes = new HashMap<String,String>();
 
     /**************************************************************************
      * Default constructor.
@@ -187,13 +196,24 @@
      **************************************************************************/
     public MobyParser (String lowestKnownDataType) {
 	super();
-	if ("".equals (lowestKnownDataType))
+	if (StringUtils.isBlank (lowestKnownDataType))
 	    this.lowestKnownDataType = null;
 	else
 	    this.lowestKnownDataType = lowestKnownDataType;
     }
 
     /**************************************************************************
+     * Another constructor, taking more "fallback" data type names
+     * (indexed by their article names). See the full documentation at
+     * the top of this class what is "fallback" data type and when it
+     * is used.
+     **************************************************************************/
+    public MobyParser (Map<String,String> lowestKnownDataTypes) {
+	super();
+	this.lowestKnownDataTypes = lowestKnownDataTypes;
+    }
+
+    /**************************************************************************
      * Parse the contents of the given file.
      **************************************************************************/
     public MobyPackage parse (String xmlFilename)
@@ -251,13 +271,13 @@
 	MOBYBOOLEAN,
 	MOBYDATETIME
     };
-    static HashSet pcdataNames = new HashSet();
+    static Set<String> pcdataNames = new HashSet<String>();
     static {
 	for (int i = 0; i < pcdataNamesArray.length; i++) {
 	    pcdataNames.add (pcdataNamesArray[i]);
 	}
     }
-    static HashSet pcdataNamesForPrimitives = new HashSet();
+    static Set<String> pcdataNamesForPrimitives = new HashSet<String>();
     static {
 	for (int i = 0; i < pcdataNamesArrayForPrimitives.length; i++) {
 	    pcdataNamesForPrimitives.add (pcdataNamesArrayForPrimitives[i]);
@@ -273,8 +293,8 @@
      ********************************************************************/
 
     MapDataTypesIfc mapDataTypes;  // dynamically created (org.biomoby.shared.datatypes.MapDataTypes)
-    Stack objectStack;  // it has elements of type Object
-    Stack pcdataStack;  // it has elements of type StringBuffer
+    Stack<Object> objectStack;  // it has elements of type Object
+    Stack<StringBuffer> pcdataStack;  // it has elements of type StringBuffer
     boolean readingMobyObject;   // true if inside Simple
     boolean readingXrefs;        // true if inside CrossReference
     boolean readingProvision;    // true if inside Provision[Information]
@@ -313,8 +333,8 @@
 		("Class '" + MAPPING_CLASS + "' was not found.\n" +
 		 "It may indicate that you have not generated all Biomoby data types from a Biomoby registry.\n" +
 		 "See http://www.biomoby.org/moby-live/Java/docs/Moses.html for details.\n" +
-		 "If you are a jMoby developer just type: ./build-dev.sh moses-datatypes.\n" +
-		 "Or perhaps, they just need to be compiled: ./build-dev.sh moses-compile.");
+		 "If you are a jMoby developer just type: ant moses-datatypes.\n" +
+		 "Or perhaps, they just need to be compiled: ant moses-compile.");
 	}
     }
 
@@ -398,8 +418,8 @@
      ********************************************************************/
     public void startDocument()
 	throws SAXException {
-	objectStack = new Stack();
-	pcdataStack = new Stack();
+	objectStack = new Stack<Object>();
+	pcdataStack = new Stack<StringBuffer>();
 	ignoring = 0;
     }
 
@@ -517,23 +537,47 @@
 		//
 		try {
 		    Class theClass = mapDataTypes.getClass (name);
+		    if (theClass == null) {
 
-		    // start a substitution mode?
-		    if (theClass == null && ! inSubstitution) {
-
-			// makes sense only for 'top-level' objects
 			Object obj = objectStack.peek();
+
+			// is this a 'top-level' object?
 			if (obj instanceof MobySimple) {
 
-			    // ...and only if we have a substitutee
-			    if (lowestKnownDataType != null)
+			    // ...and only if we have a substitute
+			    if (lowestKnownDataType != null) {
 				theClass = mapDataTypes.getClass (lowestKnownDataType);
+			    // ...or more substitutes
+			    } else {
+				String articleName = ((MobySimple)obj).getName();
+				if (articleName != null &&
+				    lowestKnownDataTypes.containsKey (articleName)) {
+				    theClass =
+					mapDataTypes.getClass (lowestKnownDataTypes.get (articleName));
+				}
+			    }
 
 			    // ...whose Class is known to us
 			    if (theClass != null) {
 				inSubstitution = true;
-				log.warn ("Warning: '" + name +
-					  "' substituted by '" + lowestKnownDataType + "'.");
+				if (log.isWarnEnabled()) {
+				    log.warn ("Object '" + name +
+					      "' substituted by '" +
+					      theClass.getSimpleName() + "'.");
+				}
+			    }
+
+			} else {
+			    // no, it is a member (and an unknown one)
+			    String articleName = getValue (attrs, ARTICLENAME);
+			    theClass = articleName2Class (obj, articleName);
+			    if (theClass != null && log.isWarnEnabled()) {
+				log.warn ("Object '" + obj.getClass().getSimpleName() +
+					  "' has an unknown member '" + name +
+					  "' (article name '" + articleName +
+					  "'). Substituted by '" +
+					  theClass.getSimpleName() +
+					  "'.");
 			    }
 			}
 		    }
@@ -546,7 +590,9 @@
 			mobyObj.setName (getValue (attrs, ARTICLENAME));
 			objectStack.push (mobyObj);
 		    } else {
-			ignoring++;   // or the same: ignoring = 1
+			// if we still do not have any class from this
+			// element, we ignore it - and also all its children
+			ignoring++;
 			if (! inSubstitution)
 			    log.warn ("Ignoring unknown element '" + name + "'.");
 		    }
@@ -663,44 +709,41 @@
 		    ((MobyProvisionInfo)vPeek (MobyProvisionInfo.class)).setComment (new String ((StringBuffer)obj));
 		}
 
+	    } else {
 		//
 		// finally, here we deal with the real data objects
 		//
-	    } else {
-		try {
-		    if (mapDataTypes.getClass (name) != null || inSubstitution) {
 
-			if (inSubstitution && name.equals (lowestKnownDataType))
-			    inSubstitution = false;
+		// this is the just-finished MobyObject
+		MobyObject mobyObj = (MobyObject)objectStack.pop();
 
-			MobyObject mobyObj = (MobyObject)objectStack.pop();  // this is just-finished MobyObject
+		try {
 
-			// primitive types may have a PCDATA value
-			if (pcdataNamesForPrimitives.contains (name)) {
-			    String value = new String ((StringBuffer)pcdataStack.pop());
-			    mobyObj.setValue (value);
-			}
+		    // primitive types may have a PCDATA value
+		    if (pcdataNamesForPrimitives.contains (name)) {
+			String value = new String (pcdataStack.pop());
+			mobyObj.setValue (value);
+		    }
 
-			// put just-finished MobyObject into its container
-			obj2 = objectStack.peek();
-			if (obj2 instanceof MobySimple) {
-			    ((MobySimple)obj2).setData (mobyObj);
-			} else {
-			    String methodName = articleName2methodName (mobyObj);
-			    try {
-				callMethod ((MobyObject)obj2, methodName, mobyObj);
-			    } catch (SAXException e2) {
-				// perhaps we should just ignore it here and go on... I don't know
-				String msg = "Object " + name +
-				    ": either missing or unknown article name '" +
-				    mobyObj.getName() + "'.";
-				log.error (msg);
-				String s = System.getProperty (BIOMOBY_PARSER_FORGIVING);
-				if ( s == null || new Boolean (s).booleanValue() == false )
-				    throw error (msg);
-			    }
+		    // put just-finished MobyObject into its container
+		    obj2 = objectStack.peek();
+		    if (obj2 instanceof MobySimple) {
+			((MobySimple)obj2).setData (mobyObj);
+			// ...and forget about (potential) substitution
+			inSubstitution = false;
+
+		    } else {
+			String methodName = articleName2methodName (mobyObj);
+			try {
+			    callMethod ((MobyObject)obj2, methodName, mobyObj);
+			} catch (SAXException e2) {
+			    log.warn ("Object type '" + name +
+				      "' in object '" + obj2.getClass().getSimpleName() +
+				      "' (or in its child), with article name '" + mobyObj.getName() +
+				      "', is ignored.");
 			}
 		    }
+
 		} catch (MobyException e) {
 		    throw error (e.getMessage());
 		}
@@ -781,7 +824,7 @@
 	// ignore white-spaces, and text where should not be any
         if (pcdataStack.empty()) return;
 
-        StringBuffer buf = (StringBuffer)pcdataStack.peek();
+        StringBuffer buf = pcdataStack.peek();
         buf.append (ch, start, length);
     }
 
@@ -827,6 +870,52 @@
     }
 
     /*********************************************************************
+     * An 'obj' should be a MobyObject instance that should have a
+     * method for setting given 'articleName'. If not return null. If
+     * yes, use reflection to find what is the return type of this
+     * method and return such class.
+     *
+     * This is used when an object has an unknown member (its type is
+     * unknown because it can be a more specialized one, but its
+     * article name is known).
+     ********************************************************************/
+    static protected Class articleName2Class (Object obj,
+					      String articleName) {
+	if (! (obj instanceof MobyObject)) {
+	    log.error ("Unexpected object of type '" + obj.getClass().getName() +
+		       "' when a MobyObj was expected.");
+	    return null;
+	}
+	if (StringUtils.isBlank (articleName)) {
+	    log.error ("An unknown member found in object '" +
+		       obj.getClass().getName() +
+		       "' that even does not have any article name.");
+	    return null;
+	}
+
+	// this is the method we are looking for
+	String methodName =
+	    "getMoby_" + Utils.mobyEscape (Utils.javaEscape (articleName.trim()));
+
+	// here are all methods of the class whose member is unknown
+	for (Method method: obj.getClass().getMethods()) {
+	    if (! methodName.equals (method.getName()))
+		continue;
+	    Class returnType = method.getReturnType();
+	    if (returnType.isArray()) {
+		return returnType.getComponentType();
+	    } else {
+		return returnType;
+	    }
+	}
+	log.error ("An unknown member found in object '" +
+		   obj.getClass().getName() +
+		   "' with unrecognized article name '" +
+		   articleName + "'.");
+	return null;
+    }
+
+    /*********************************************************************
      * Call a method (named 'methodName') on object 'actor', using
      * 'parameter'.
      ********************************************************************/




More information about the MOBY-guts mailing list