001    /*
002    @license.text@
003     */
004    package biz.hammurapi.ant;
005    
006    import java.lang.reflect.InvocationTargetException;
007    import java.util.Collection;
008    import java.util.Iterator;
009    import java.util.LinkedList;
010    import java.util.List;
011    
012    import org.apache.tools.ant.AntClassLoader;
013    import org.apache.tools.ant.BuildException;
014    import org.apache.tools.ant.Task;
015    import org.apache.tools.ant.types.Path;
016    
017    import biz.hammurapi.config.ConfigurationException;
018    import biz.hammurapi.config.Parameterizable;
019    
020    /**
021     * Object entry. Base class for configurable objects. 
022     * @ant.element name="objectentry"
023     * @author Pavel Vlasov 
024     * @version $Revision: 1.5 $
025     */
026    public class ObjectEntry extends Task {
027            private String className;
028            private String value;
029            private List parameters=new LinkedList();
030            
031            /**
032             * Configuration parameter. Object entry class must implement biz.hammurapi.config.Parameterizable
033             * @ant.non-required
034             * @param parameter
035             * @throws BuildException
036             */
037            public void addConfiguredParameter(Param parameter) throws BuildException {
038                    if (parameter.getName()==null) {
039                            throw new BuildException("Unnamed parameter");
040                    }
041                    parameters.add(parameter);
042            }               
043    
044            /**
045             * Either value or class name is required. If both class name and value are
046             * specified then that class should have a constructor with one String parameter.
047             * @ant.non-required
048             * @param className
049             */
050            public void setClassName(String className) {
051                    this.className = className;
052            }
053    
054            /**
055             * Not required if class name is set.
056             * @ant.non-required
057             * @param value
058             */
059            public void setValue(String value) {
060                    this.value = value;
061            }
062    
063            /**
064             * @ant.ignore
065             * @return
066             */
067            public Collection getParameters() {
068                    return parameters;
069            }
070            
071            private Object theObject;
072            
073            protected void validateClass(Class clazz) throws BuildException {
074                    
075            }
076            
077            protected void validateInstance(Object instance) throws BuildException {
078                    
079            }
080            
081            public Object getObject(ClassLoader masterClassLoader) throws BuildException {
082                    if (theObject==null) {
083                            if (className==null) {
084                                    if (parameters.isEmpty()) {
085                                            if (value==null) {
086                                                    return null;
087                                            }
088                                            theObject=value;
089                                    } else {
090                                            throw new BuildException("Nested parameters supported only if classname attribute is set");
091                                    }
092                            } else {
093                                    try {
094                                            ClassLoader classLoader=masterClassLoader==null ? getProject().getCoreLoader() : masterClassLoader;
095                                            if (classPath!=null) {
096                                                    if (masterClassLoader==null) {
097                                                            classLoader=new AntClassLoader(getProject(), classPath);
098                                                    } else {
099                                                            classLoader=new AntClassLoader(masterClassLoader, getProject(), classPath, false);
100                                                    }
101                                            }
102                                            
103                                            if (classLoader==null) {
104                                                    classLoader=getClass().getClassLoader();
105                                            }
106                                            
107                                            Class theClass=classLoader.loadClass(className);
108                                            validateClass(theClass);
109                                            if (!(parameters.isEmpty() || Parameterizable.class.isAssignableFrom(theClass))) {
110                                                    throw new BuildException(className+" does not support parameters");
111                                            }
112                                            
113                                            if (value==null) {
114                                                    theObject=theClass.newInstance();
115                                            } else {
116                                                    theObject=theClass.getConstructor(new Class[] {String.class}).newInstance(new Object[] {value});
117                                            }
118                                            
119                                            if (!parameters.isEmpty()) {
120                                                    // Double check
121                                                    if (theObject instanceof Parameterizable) {
122                                                            Iterator it=parameters.iterator();
123                                                            while (it.hasNext()) {
124                                                                    Param param=(Param) it.next();
125                                                                    try {
126                                                                            if (!((Parameterizable) theObject).setParameter(param.getName(), param.getObject(masterClassLoader))) {
127                                                                                throw new BuildException(theObject.getClass().getName()+" does not support parameter "+param.getName());
128                                                                            }
129                                                                    } catch (ConfigurationException e) {
130                                                                            throw new BuildException("Could not set parameter "+param.getName()+" for object entry "+theObject.getClass().getName(), e);
131                                                                    }
132                                                            }
133                                                    } else {                                                
134                                                            throw new BuildException(className+" does not support parameters");
135                                                    }
136                                            }
137                                            validateInstance(theObject);
138                                    } catch (ClassNotFoundException e) {
139                                            throw new BuildException("Class not found: "+className, e);
140                                    } catch (InstantiationException e) {
141                                            throw new BuildException("Can not instantiate: "+className, e);
142                                    } catch (IllegalAccessException e) {
143                                            throw new BuildException("Can not instantiate: "+className, e);
144                                    } catch (InvocationTargetException e) {
145                                            throw new BuildException("Can not instantiate: "+className, e);
146                                    } catch (NoSuchMethodException e) {
147                                            throw new BuildException("Constructor "+className+"(String) not found", e);
148                                    }       
149                            }
150                    }
151                    return theObject;
152            }
153            /**
154             * @return
155             */
156            protected String getClassName() {
157                    return className;
158            }
159    
160            /**
161             * @return
162             */
163            protected String getValue() {
164                    return value;
165            }
166            
167            /**
168             * Classpath for loading classes
169             * @ant:non-required 
170             */
171            private Path classPath;
172            
173            public void setClassPath(Path classPath) {
174                    if (this.classPath == null) {
175                            this.classPath = classPath;
176                    } else {
177                            this.classPath.append(classPath);
178                    }
179            }
180    
181            /**
182             * Maybe creates a nested classpath element.
183             * @ant:non-required
184             */
185            public Path createClasspath() {
186                    if (classPath == null) {
187                            classPath = new Path(getProject());
188                    }
189                    return classPath.createPath();
190            }       
191            
192            public void addText(String text) {
193                    this.value=text;
194            }
195    }