KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jicengine > element > ElementCompiler


1 package org.jicengine.element;
2
3 import org.jicengine.operation.StaticValue;
4 import org.jicengine.operation.OperationException;
5 import org.jicengine.operation.VariableValueOperation;
6 import org.jicengine.operation.Operation;
7 import org.jicengine.operation.EmptyOperation;
8 import org.jicengine.expression.LJEParser;
9 import org.jicengine.expression.ClassParser;
10 import org.jicengine.expression.SyntaxException;
11 import java.util.*;
12
13 /**
14  * <p>
15  * A class that makes it possible to create Element-objects little by little, as
16  * is needed if the JIC-file is parsed with a SAX-parser.
17  * </p>
18  * <p>
19  * ElementCompiler parses String-typed attributes and other data to corresponding
20  * objects. After all the data of an XML-element has been processed, the resulting
21  * runtime-Element can be obtained with method <code>createElement()</code>.
22  * </p>
23  * <p>
24  *
25  * </p>
26  *
27  * <h4> Element Life cycle </h4>
28  *
29  * <p>
30  * Copyright (C) 2004 Timo Laitinen
31  * </p>
32  * @author .timo
33  */

34
35 public abstract class ElementCompiler {
36
37     public static final String JavaDoc ATTR_NAME_ACTION = "action";
38     public static final String JavaDoc ATTR_NAME_CLASS = "class";
39     public static final String JavaDoc ATTR_NAME_VARIABLES = "vars";
40     public static final String JavaDoc ATTR_NAME_CONSTRUCTOR_ARGUMENTS = "args";
41     public static final String JavaDoc ATTR_NAME_TRACE = "trace";
42     public static final String JavaDoc ATTR_NAME_TYPE = "type";
43     public static final String JavaDoc ATTR_NAME_INSTANCE = "instance";
44     public static final String JavaDoc ATTR_NAME_OVERRIDABLE_BY = "overridable-by";
45     public static final String JavaDoc ATTR_NAME_IF = "if";
46
47     public ElementCompiler()
48     {
49     }
50
51     private ElementImpl element;
52     private String JavaDoc constructorArguments;
53     private boolean constructorDerivedFromClassInformation = false;
54
55
56     public ElementCompiler(String JavaDoc name, Location location)
57     {
58         this.element = new ElementImpl(name, location);
59     }
60
61   public Element createElement() throws ElementException
62   {
63     return getElement().toRuntimeElement();
64   }
65   
66     public String JavaDoc getName()
67     {
68         return getElement().getName();
69     }
70
71     public Location getLocation()
72     {
73         return getElement().getLocation();
74     }
75
76     public void setOverridableBy(String JavaDoc overridingId)
77     {
78         getElement().setOverridableBy(overridingId);
79     }
80
81     public void setIf(String JavaDoc condition) throws ElementException
82     {
83         try {
84             getElement().setIf(LJEParser.getInstance().parse(condition));
85         } catch (SyntaxException e){
86             throw new AttributeException("[if=\"" + condition + "\"]: " + e.getMessage(), e, getName(), getLocation());
87         }
88     }
89
90
91     /**
92      *
93      */

94     public void setVariables(String JavaDoc variableExpression) throws ElementException
95     {
96         StringTokenizer tokenizer = new StringTokenizer(variableExpression, ",");
97         String JavaDoc[] variableNames = new String JavaDoc[tokenizer.countTokens()];
98         int i = 0;
99         while(tokenizer.hasMoreElements() ){
100             variableNames[i] = tokenizer.nextToken().trim();
101             i++;
102         }
103         setVariables(variableNames);
104     }
105
106     public void setVariables(String JavaDoc[] variableNames) throws ElementException
107     {
108         getElement().setVariableNames(variableNames);
109     }
110
111     public void setConstructorArguments(String JavaDoc argumentExpression)
112     {
113         this.constructorArguments = argumentExpression;
114     }
115
116     public void setConstructor(String JavaDoc expression) throws ElementException
117     {
118         try {
119             getElement().setConstructor(LJEParser.getInstance().parse(expression));
120         } catch (SyntaxException e){
121             throw new AttributeException("[instance=\"" + expression + "\"]: " + e.getMessage(), e, getName(), getLocation());
122         }
123     }
124
125     public Operation getConstructor()
126     {
127         return getElement().getConstructor();
128     }
129
130     public void setInstanceClass(String JavaDoc className) throws ElementException
131     {
132         try {
133             getElement().setInstanceClass(ClassParser.INSTANCE.toClass(className));
134         } catch(Exception JavaDoc e) {
135             throw new AttributeException("[class=\"" + className + "\"]: " + e.getMessage(),e, getName(), getLocation());
136         }
137     }
138
139     /**
140      * <p>
141      * for setting the action as an expression.
142      * </p>
143      * <p>
144      * enhancements:
145      * </p>
146      * <ul>
147      * <li>empty string is mapped into an EmptyOperation</li>
148      * <li>
149      * expressions of type <code>setMethod(value)</code> and
150      * <code>add(component)</code> are automatically madded to expressions
151      * <code>parent.setMethod(value)</code> and
152      * <code>parent.add(component)</code>.
153      * </li>
154      * </ul>
155      */

156     public void setAction(String JavaDoc expression) throws ElementException
157     {
158         if(expression.length() == 0) {
159             getElement().setAction(EmptyOperation.INSTANCE);
160         }
161         else {
162             String JavaDoc preparedExpression;
163             int paramStart = expression.indexOf("(");
164             int commaIndex = -1;
165
166             if( paramStart != -1 ){
167                 commaIndex = expression.substring(0,paramStart).indexOf(".");
168             }
169
170             // the existence of '(' implies that the action is indeed a method call
171
// (what else?)
172
// the lack of '.' implies that the actor of the method call is missing.
173
// therefore we add the implicit actor parent into the expression.
174
if(paramStart != -1 && commaIndex == -1) {
175                 preparedExpression = Element.VARIABLE_NAME_PARENT_INSTANCE + "." + expression;
176             }
177             else {
178                 preparedExpression = expression;
179             }
180
181             try {
182                 getElement().setAction(LJEParser.getInstance().parse(preparedExpression));
183             } catch (SyntaxException e){
184                 throw new AttributeException("[action=\"" + expression + "\"]: " + e.getMessage(), e, getName(), getLocation());
185             }
186         }
187     }
188
189
190     /**
191      * if this element has a constructor, the cdata is added to the element-context
192      * with the name 'cdata'. (cdata might be ignored if the constructor doesn't
193      * use cdata) if there is no constructor, cdata becomes the value of this
194      * element.
195      *
196      * @param cdata Description of the Parameter
197    * @param syntaxBasedCdataConversionsSupported
198      * @throws ElementException Description of the Exception
199      */

200     public void setCData(String JavaDoc cdata, boolean syntaxBasedCdataConversionsSupported) throws ElementException
201     {
202         if( getElement().getConstructor() == null || this.constructorDerivedFromClassInformation ){
203             // there is no real constructor yet.
204
// in this case the element instance is derived from the CDATA.
205
// obtain a constructor that converts the CDATA-string into the required
206
// object.
207

208       if( getElement().getInstanceClass() == null ){
209         // the class needs to be specified.
210

211         Class JavaDoc instanceClass;
212         
213         if( syntaxBasedCdataConversionsSupported){
214           // we therefore resolve the class by examining the syntax of the CDATA
215
instanceClass = CdataHandler.resolveInstanceClassFromCdata(cdata);
216         }
217         else {
218           // JICE 2.0 behaviour:
219
instanceClass = String JavaDoc.class;
220         }
221         
222         // setting the instance class is enough:
223
// the CDATA will now be handled as a normal
224
// CDATA conversion.
225
getElement().setInstanceClass(instanceClass);
226       }
227
228             Operation constructor;
229             try {
230                 constructor = CdataHandler.getClassBasedCdataConversionConstructor(getElement().getInstanceClass(), cdata);
231             } catch (Exception JavaDoc ex) {
232                 throw new ElementException(ex, getName(), getLocation());
233             }
234             getElement().deleteConstructor();
235             getElement().setConstructor(constructor);
236
237             // note: we don't save the CDATA anywhere here. we assume that
238
// the constructor obtained from CdataHandler has stored the CDATA.
239

240         }
241         else {
242             // a constructor has been specified explicitly.
243

244             if( getElement().isConstructorVariable(Element.VARIABLE_NAME_CDATA) ){
245                 // ok, the constructor consumes the CDATA.
246

247                 // we create a virtual element that makes it possible to handle the
248
// CDATA like any other child element..
249
VariableElement virtualElement = new StaticValueElement(Element.VARIABLE_NAME_CDATA, getLocation(), cdata);
250                 handleChildElement(virtualElement);
251             }
252             else {
253                 // the constructor doesn't use the CDATA. It is therefore illegal!
254
throw new ElementException("Illegal CDATA: CDATA must be used in the constructor (variable 'cdata').", getName(), getLocation());
255             }
256         }
257     }
258
259   /**
260    * <p>
261    * Called by handler when the start-tag of the element has been processed -
262    * Element has been created and all attributes have been set, but no CDATA nor
263    * child-elements have been processed.
264    * </p>
265    * <p>
266    * This is a good spot for verifying that
267    * the state of the element is valid - the attributes have been used properly,
268    * attributes not set by user can be set to their default values, etc.
269    * </p>
270    *
271    * @throws ElementException Description of the Exception
272    */

273   public void elementInitialized() throws ElementException
274   {
275     // do some validity checks concerning the use of the 'args' attribute.
276
if(this.constructorArguments != null) {
277       if(getElement().getInstanceClass() == null) {
278         throw new AttributeException("Attribute '" + ATTR_NAME_CONSTRUCTOR_ARGUMENTS + "' must be specified together with attribute '" + ATTR_NAME_CLASS + "'", getName(), getLocation());
279       }
280       if(getElement().getConstructor() != null) {
281         throw new AttributeException("Attributes '" + ATTR_NAME_INSTANCE + "' and '" + ATTR_NAME_CONSTRUCTOR_ARGUMENTS + "' can't be used together", getName(), getLocation());
282       }
283     }
284
285     if( getElement().getConstructor() != null ){
286       // the element has a constructor
287
// -> it has an object
288
// -> the class of object must be specified!
289
if( getElement().getInstanceClass() == null ){
290         throw new AttributeException("The attribute '" + ATTR_NAME_CLASS + "' must be specified.", getName(), getLocation());
291       }
292     }
293
294
295     deriveConstructorFromClassInformation();
296   }
297   
298     /**
299      * Used for notifying this element about a child-element
300      *
301      * @param child a child with no action, can have a value or not.
302      * @throws ElementException Description of the Exception
303      */

304     public void handleChildElement(Element child) throws ElementException
305     {
306         if( child instanceof VariableElement && !getElement().isUsed((VariableElement)child) ){
307             // this value element has no use.. yet.
308
// we let the subclasses to decide the purpose of this child.
309
//
310
ActionElement actionElement = handleLooseVariableElement((VariableElement)child);
311
312             // now the child is an action element and we can add it.
313
getElement().addChildElement(actionElement);
314         }
315         else {
316             getElement().addChildElement(child);
317         }
318     }
319
320   protected ElementImpl getElement()
321   {
322     return this.element;
323   }
324   
325     /**
326      *
327      * for subclasses!
328      */

329     protected abstract ActionElement handleLooseVariableElement(VariableElement child) throws ElementException;
330
331
332
333     public String JavaDoc toString()
334     {
335         return "<" + getName() + ">";
336     }
337
338     /**
339      * <p>
340      * create the implicit constructor from 'class' and 'args' attributes,
341      * if possible. if the element already has a constructor, nothing is done.
342      * </p>
343      *
344      *
345      * @throws ElementException Description of the Exception
346      */

347     private void deriveConstructorFromClassInformation() throws ElementException
348     {
349         if( getElement().getConstructor() == null && getElement().getInstanceClass() != null ){
350             // we use the instanceClass-information for constructing the implicit constructor.
351

352             String JavaDoc constructorExpression;
353             if(this.constructorArguments == null) {
354                 // empty constructor
355
constructorExpression = "new " + getElement().getInstanceClass().getName() + "()";
356                 // we mark that the constructor wasn't explicitly set in the code
357
// but was an implicit constructor derived from the attributes.
358
// we need this information when handling cdata..
359
this.constructorDerivedFromClassInformation = true;
360             }
361             else {
362                 // we have some parameters.
363
constructorExpression = "new " + getElement().getInstanceClass().getName() + "(" + this.constructorArguments + ")";
364                 this.constructorDerivedFromClassInformation = false;
365             }
366
367             // TODO: don't create a string to be parsed - create a ready Operation-object
368

369             try {
370                 setConstructor(constructorExpression);
371
372             } catch (ElementException e){
373                 throw new ElementException("Problems creating implicit constructor.", e, getName(), getLocation());
374             }
375         }
376     }
377
378 }
379
Popular Tags