Ok, big merge. here's the new xml-config stuff in action. There's a few
[mir.git] / source / mir / xml / XmlConfigurator.java
index a67a7c4..101d659 100755 (executable)
@@ -2,6 +2,7 @@ package mir.xml;
 
 import java.io.*;
 import java.util.*;
+import java.lang.reflect.*;
 import org.xml.sax.helpers.DefaultHandler;
 import org.xml.sax.*;
 import javax.xml.parsers.ParserConfigurationException;
@@ -22,6 +23,9 @@ import mir.misc.Location;
  * classes as well as the inclusion of a method to
  * add parameters (nested tags) that are required.
  * (the addRequired method) in the config file.
+ * that part is from tomcat.
+ *
+ * much code is stolen from ant ProjectHelper.java.
  *
  * @author -mh <heckmann@hbe.ca>
  * @version 2001.10.21
@@ -37,21 +41,46 @@ public class XmlConfigurator {
     private File configFileParent;
     private Locator locator;
 
-    //private SaxContext saxContext;
+    private SaxContext saxContext;
+
+    XmlMatch requiredXmlMatch[]=new XmlMatch[256]; //maximum amount of rules
+    int requiredXmlMatchCount=0;
+    boolean matched[] = new boolean[256];
+    int matchedCount=0;
+
+    XmlMatch mustComeFirstMatch[]=new XmlMatch[256]; //maximum amount of rules
+    int comeFirstMatchCount=0;
+
+    Property comesFirstArr[]=new Property[128];
+    int comesFirstCount=0;
+
+    Property propertyArr[]=new Property[128];
+    int propertyCount=0;
+
+    private static XmlConfigurator instance = new XmlConfigurator();
+    public static XmlConfigurator getInstance() { return instance; }
 
     /**
      * Configures the Project with the contents of the specified XML file.
      */
-    public static void configure(File configFile) throws ConfigException {
-        new XmlConfigurator(configFile).parse();
+    public void configure(File configFile) throws ConfigException {
+        setConfigFile(configFile);
+        parse();
     }
 
     /**
+     * konstruktor. private so no one calls "new" on us.
+     */
+    private XmlConfigurator() {}
+
+
+    /**
      * Constructs a new Ant parser for the specified XML file.
      */
-    private XmlConfigurator(File configFile) {
+    private void setConfigFile(File configFile) {
         this.configFile = new File(configFile.getAbsolutePath());
         configFileParent = new File(this.configFile.getParent());
+        saxContext = new SaxContext();
     }
 
     /**
@@ -74,7 +103,24 @@ public class XmlConfigurator {
             inputSource = new InputSource(inputStream);
             inputSource.setSystemId(uri);
             saxParser.parse(inputSource, new RootHandler());
-        }
+            if(matchedCount < requiredXmlMatchCount) {
+                for( int i=0; i<requiredXmlMatchCount; i++) {
+                    if( !matched[i] )
+                        throw new ConfigException("Error parsing config file, missing required element: "+requiredXmlMatch[i].toString());
+                }
+            }
+            try {
+                for(int i=0; i<comesFirstCount;i++) {
+                    comesFirstArr[i].set();
+                }
+                for(int i=0; i<propertyCount;i++) {
+                    propertyArr[i].set();
+                    System.out.println("about to set: "+i);
+                }
+            } catch (Exception e) {
+                throw new SAXParseException(e.toString(), locator);
+            }
+}
         catch(ParserConfigurationException exc) {
             throw new ConfigException("Parser has not been configured correctly", exc);
         }
@@ -163,8 +209,7 @@ public class XmlConfigurator {
          */
         protected void finished() {}
 
-        public void endElement() throws SAXException {
-
+        public void endElement(String uri, String tag, String qName) throws SAXException {
             finished();
             // Let parent resume handling SAX events
             saxParser.getXMLReader().setContentHandler(parentHandler);
@@ -259,6 +304,9 @@ public class XmlConfigurator {
                                             locator);
             }
             
+            saxContext.push(tag);
+            matchedCount += checkRequiredTag(saxContext);
+
             //MirConfig.setName(name);
 
         }
@@ -271,6 +319,11 @@ public class XmlConfigurator {
             }
         }
 
+        public void finished() {
+            System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
+            saxContext.pop();
+        }
+
         private void handleClassdef(String name, Attributes attrs) throws SAXParseException {
             (new ClassHandler(this)).init(name, attrs);
         }
@@ -306,8 +359,10 @@ public class XmlConfigurator {
                 throw new SAXParseException("class element appears without a \"name\" attribute", locator);
             }
 
+            saxContext.push(tag+":"+name);
+            matchedCount += checkRequiredTag(saxContext);
             try {
-                classN=Class.forName(name);
+                classN=Class.forName(name, false, this.getClass().getClassLoader());
             } catch (ClassNotFoundException e) {
                 throw new ConfigException("Error invoking class: \""+name+
                     "\"",e);
@@ -323,6 +378,12 @@ public class XmlConfigurator {
             }
         }
 
+        public void finished() {
+            System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
+            System.out.println("COUNT "+saxContext.getTagCount());
+            saxContext.pop();
+        }
+
         private void handleProperties(String name, Attributes attrs) throws SAXParseException {
             (new PropertiesHandler(this, classN )).init(name, attrs);
         }
@@ -364,15 +425,60 @@ public class XmlConfigurator {
             if (value == null) {
                 throw new SAXParseException("property element appears without a \"value\" attribute", locator);
             }
-            /////
+            saxContext.push(tag+":"+name);
+            matchedCount += checkRequiredTag(saxContext);
+
+            //finally add it to the lists
+            //to be processed later
+            if (checkComesFirstTag(saxContext)) {
+                comesFirstArr[comesFirstCount]=new Property(classN, name, value);
+                comesFirstCount++;
+            } else {
+                propertyArr[propertyCount]=new Property(classN, name, value);
+                propertyCount++;
+            }
         }
 
         protected void finished() {
-          //do the setting here?
+            System.out.println("COUNT "+saxContext.getTagCount()+" TAG "+saxContext.getTag(saxContext.getTagCount()-1));
+            System.out.println("COUNT "+saxContext.getTagCount());
+            saxContext.pop();
         }
 
     }
 
+    public void addComesFirstTag(String xmlPath) {
+        mustComeFirstMatch[comeFirstMatchCount]=new XmlMatch(xmlPath);
+        comeFirstMatchCount++;
+    }
+
+    private boolean checkComesFirstTag(SaxContext ctx) {
+        for( int i=0; i<comeFirstMatchCount; i++ ) {
+            if( mustComeFirstMatch[i].match(ctx) ) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void addRequiredTag(String xmlPath) {
+        requiredXmlMatch[requiredXmlMatchCount]=new XmlMatch(xmlPath);
+        matched[requiredXmlMatchCount]=false;
+        requiredXmlMatchCount++;
+    }
+
+    private int checkRequiredTag(SaxContext ctx) {
+        int matchCount=0;
+        for( int i=0; i<requiredXmlMatchCount; i++ ) {
+            if( requiredXmlMatch[i].match(ctx) ) {
+                matched[i]=true;
+                matchCount++;
+            }
+        }
+        return matchCount;
+    }
+
+
     private static String capitalize(String name) {
         if (name == null || name.length() == 0) {
             return name;
@@ -382,4 +488,90 @@ public class XmlConfigurator {
         return new String(chars);
     }
 
+    private class Property {
+        Class classN;
+        String name;
+        String value;
+
+        public Property( Class classN, String name, String value) {
+            this.classN=classN;
+            this.name=name;
+            this.value=value;
+        }
+            
+        /** Find a method with the right name
+         * If found, call the method ( if param is int or boolean we'll convert 
+         * value to the right type before) - that means you can have setDebug(1).
+         */
+        public void set() throws Exception {
+            
+            String setter= "set" +capitalize(name);
+
+            try {
+                Method methods[]=classN.getMethods();
+                Method setPropertyMethod=null;
+
+                // First, the ideal case - a setFoo( String ) method
+                for( int i=0; i< methods.length; i++ ) {
+                    Class paramT[]=methods[i].getParameterTypes();
+                    if( setter.equals( methods[i].getName() ) &&
+                        paramT.length == 1 &&
+                        "java.lang.String".equals( paramT[0].getName())) {
+
+                        methods[i].invoke( null, new Object[] { value } );
+                        return;
+                    }
+                } //end for
+
+                // Try a setFoo ( int ), (float) or ( boolean )
+                for( int i=0; i< methods.length; i++ ) {
+                    boolean ok=true;
+                    if( setter.equals( methods[i].getName() ) &&
+                        methods[i].getParameterTypes().length == 1) {
+
+                        // match - find the type and invoke it
+                        Class paramType=methods[i].getParameterTypes()[0];
+                        Object params[]=new Object[1];
+                        if ("java.lang.Integer".equals( paramType.getName()) ||
+                            "int".equals( paramType.getName())) {
+                            try {
+                                params[0]=new Integer(value);
+                            } catch( NumberFormatException ex ) {ok=false;}
+                        } else if ("java.lang.Float".equals( paramType.getName()) ||
+                            "float".equals( paramType.getName())) {
+                            try {
+                                params[0]=new Float(value);
+                            } catch( NumberFormatException ex ) {ok=false;}
+                        } else if ("java.lang.Boolean".equals( paramType.getName()) ||
+                            "boolean".equals( paramType.getName())) {
+                            params[0]=new Boolean(value);
+                        } else {
+                            throw new Exception("Unknown type " + paramType.getName() + "for property \""+name+"\"with value \""+value+"\"");
+                        }
+
+                        if( ok ) {
+                            System.out.println("XXX: " + methods[i] + " " + classN + " " + params[0] );
+                            methods[i].invoke( null, params );
+                            return; 
+                        } //end if
+                    } //end if setter
+                } //end for
+
+                //if we got this far it means we were not successful in setting the
+                //property
+                throw new Exception("Count not find method \""+setter+"\" in Class \""+classN.getName()+"\" in order to set property \""+name+"\"");
+
+            } catch( SecurityException ex1 ) {
+                throw new Exception("SecurityException for " + classN.getName() + " " +  name + "="  + value  +")" );
+                //if( ctx.getDebug() > 1 ) ex1.printStackTrace();
+            } catch (IllegalAccessException iae) {
+                throw new Exception("IllegalAccessException for " + classN.getName() + " " +  name + "="  + value  +")" );
+                //if( ctx.getDebug() > 1 ) iae.printStackTrace();
+            } catch (InvocationTargetException ie) {
+                throw new Exception("InvocationTargetException for " + classN.getName() + " " +  name + "="  + value  +")" );
+                //if( ctx.getDebug() > 1 ) ie.printStackTrace();
+            }
+        }
+    }
+
 }