001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /**
004: * LZX Classes
005: */package org.openlaszlo.compiler;
006:
007: import java.util.*;
008: import org.jdom.Element;
009:
010: class ClassModel implements Comparable {
011: protected final ViewSchema schema;
012: protected final String className;
013: // This is null for builtin classes
014: protected final ClassModel super class;
015: // This is null for builtin classes
016: protected final Element definition;
017: protected NodeModel nodeModel;
018:
019: /** Set of tags that can legally be nested in this element */
020: protected Set mCanContainTags = new HashSet();
021:
022: /* If superclass is a predefined system class, just store its name. */
023: protected String super className = null;
024: protected boolean hasInputText = false;
025: protected boolean isInputText = false;
026:
027: /* Class or superclass has an <attribute type="text"/> */
028: protected boolean supportsTextAttribute = false;
029: /** Map attribute name to type */
030: protected final Map attributeSpecs = new LinkedHashMap();
031:
032: protected boolean inline = false;
033: protected String sortkey = null;
034:
035: public String toString() {
036: return "ClassModel: className=" + className + ", "
037: + "superclass=" + super class + ", " + "superclassName="
038: + super className + ", " + "hasInputText="
039: + hasInputText + ", " + "isInputText=" + isInputText
040: + ", " + "definition=" + definition;
041: }
042:
043: // Construct a user-defined class
044: ClassModel(String className, ClassModel super class,
045: ViewSchema schema, Element definition) {
046: this .className = className;
047: this .super class = super class;
048: this .definition = definition;
049: this .schema = schema;
050: this .sortkey = className;
051: if (super class != null) {
052: this .sortkey = super class.sortkey + "." + this .sortkey;
053: }
054: }
055:
056: // Construct a builtin class
057: ClassModel(String className, ViewSchema schema) {
058: this (className, null, schema, null);
059: }
060:
061: public int compareTo(Object other) throws ClassCastException {
062: ClassModel o = (ClassModel) other;
063: int order = this .sortkey.startsWith(o.sortkey) ? +1
064: : this .sortkey.compareTo(o.sortkey);
065: return order;
066: }
067:
068: public String toLZX() {
069: return toLZX("");
070: }
071:
072: public String toLZX(String indent) {
073: String lzx = indent
074: + "<interface name='"
075: + className
076: + "'"
077: + ((super class != null) ? (" extends='"
078: + super class.className + "'") : "") + ">";
079: for (Iterator i = attributeSpecs.values().iterator(); i
080: .hasNext();) {
081: AttributeSpec spec = (AttributeSpec) i.next();
082: String specLZX = spec.toLZX(indent + " ", super class);
083: if (specLZX != null) {
084: lzx += "\n";
085: lzx += specLZX;
086: }
087: }
088:
089: lzx += "\n" + indent + "</interface>";
090: return lzx;
091: }
092:
093: /** Returns true if this is equal to or a subclass of
094: * superclass. */
095: boolean isSubclassOf(ClassModel super class) {
096: if (this == super class)
097: return true;
098: if (this .super class == null)
099: return false;
100: return this .super class.isSubclassOf(super class);
101: }
102:
103: boolean isBuiltin() {
104: return super class == null;
105: }
106:
107: boolean hasNodeModel() {
108: // Classes that have generated code will have a nodeModel
109: return nodeModel != null;
110: }
111:
112: ClassModel getSuperclassModel() {
113: return super class;
114: }
115:
116: String getClassName() {
117: return this .className;
118: }
119:
120: String getSuperclassName() {
121: if (super className != null) {
122: return super className;
123: } else if (super class == null) {
124: return null;
125: } else {
126: return super class.className;
127: }
128: }
129:
130: /** Return the AttributeSpec for the attribute named attrName.
131: Only returns locally defined attribute, does not follow up the
132: class hierarchy.
133: */
134: AttributeSpec getLocalAttribute(String attrName) {
135: return (AttributeSpec) attributeSpecs.get(attrName);
136: }
137:
138: /** Return the AttributeSpec for the attribute named attrName. If
139: * the attribute is not defined on this class, look up the
140: * superclass chain.
141: */
142: AttributeSpec getAttribute(String attrName) {
143: AttributeSpec attr = (AttributeSpec) attributeSpecs
144: .get(attrName);
145: if (attr != null) {
146: return attr;
147: } else if (super class != null) {
148: return (super class.getAttribute(attrName));
149: } else {
150: return null;
151: }
152: }
153:
154: /** Find an attribute name which is similar to attrName, or return
155: * null. Used in compiler warnings. */
156: AttributeSpec findSimilarAttribute(String attrName) {
157: for (Iterator iter = attributeSpecs.values().iterator(); iter
158: .hasNext();) {
159: AttributeSpec attr = (AttributeSpec) iter.next();
160: if ((attrName.toLowerCase().equals(attr.name.toLowerCase()))
161: || (attrName.toLowerCase().startsWith(attr.name
162: .toLowerCase()))
163: || (attrName.toLowerCase().endsWith(attr.name
164: .toLowerCase()))
165: || (attr.name.toLowerCase().startsWith(attrName
166: .toLowerCase()))
167: || (attr.name.toLowerCase().endsWith(attrName
168: .toLowerCase()))) {
169: return attr;
170: }
171: }
172: // if that didn't work, try the supeclass
173: if (super class == null) {
174: return null;
175: } else {
176: return super class.findSimilarAttribute(attrName);
177: }
178: }
179:
180: ViewSchema.Type getAttributeTypeOrException(String attrName)
181: throws UnknownAttributeException {
182: AttributeSpec attr = getAttribute(attrName);
183: if (attr != null) {
184: return attr.type;
185: }
186: // If there is no superclass attribute, use the default static
187: // attribute map
188: ViewSchema.Type type = ViewSchema.getAttributeType(attrName);
189: // Last resort, use default of 'expression' type
190: if (type == null) {
191: throw new UnknownAttributeException();
192: }
193: return type;
194: }
195:
196: ViewSchema.Type getAttributeType(String attrName) {
197: AttributeSpec attr = getAttribute(attrName);
198: if (attr != null) {
199: return attr.type;
200: }
201: // If there is no superclass attribute, use the default static
202: // attribute map
203: ViewSchema.Type type = ViewSchema.getAttributeType(attrName);
204: // Last resort, use default of 'expression' type
205: if (type == null) {
206: type = ViewSchema.EXPRESSION_TYPE;
207: }
208: return type;
209: }
210:
211: void setNodeModel(NodeModel model) {
212: this .nodeModel = model;
213: }
214:
215: boolean getInline() {
216: return inline && nodeModel != null;
217: }
218:
219: void setInline(boolean inline) {
220: this .inline = inline;
221: }
222:
223: public static class InlineClassError extends CompilationError {
224: public InlineClassError(ClassModel cm, NodeModel im,
225: String message) {
226: super (
227: "The class "
228: + cm.className
229: + " has been declared "
230: + "inline-only but cannot be inlined. "
231: + message
232: + ". "
233: + "Remove "
234: + cm.className
235: + " from the <?lzc class=\""
236: + cm.className
237: + "\"> or "
238: + "<?lzc classes=\""
239: + cm.className
240: + "\"> processing instruction to remove this error.",
241: im.element);
242: }
243: }
244:
245: protected boolean descendantDefinesAttribute(NodeModel model,
246: String name) {
247: for (Iterator iter = model.getChildren().iterator(); iter
248: .hasNext();) {
249: NodeModel child = (NodeModel) iter.next();
250: if (child.hasAttribute(name)
251: || descendantDefinesAttribute(child, name))
252: return true;
253: }
254: return false;
255: }
256:
257: NodeModel applyClass(NodeModel instance) {
258: final String DEFAULTPLACEMENT_ATTR_NAME = "defaultPlacement";
259: final String PLACEMENT_ATTR_NAME = "placement";
260: if (nodeModel == null)
261: throw new RuntimeException("no nodeModel for " + className);
262: if (nodeModel.hasAttribute(DEFAULTPLACEMENT_ATTR_NAME))
263: throw new InlineClassError(this , instance,
264: /* (non-Javadoc)
265: * @i18n.test
266: * @org-mes="The class has a " + p[0] + " attribute"
267: */
268: org.openlaszlo.i18n.LaszloMessages.getMessage(
269: ClassModel.class.getName(), "051018-196",
270: new Object[] { DEFAULTPLACEMENT_ATTR_NAME }));
271: if (instance.hasAttribute(DEFAULTPLACEMENT_ATTR_NAME))
272: throw new InlineClassError(this , instance,
273: /* (non-Javadoc)
274: * @i18n.test
275: * @org-mes="The instance has a " + p[0] + " attribute"
276: */
277: org.openlaszlo.i18n.LaszloMessages.getMessage(
278: ClassModel.class.getName(), "051018-205",
279: new Object[] { DEFAULTPLACEMENT_ATTR_NAME }));
280: if (descendantDefinesAttribute(instance, PLACEMENT_ATTR_NAME))
281: throw new InlineClassError(this , instance,
282: /* (non-Javadoc)
283: * @i18n.test
284: * @org-mes="An element within the instance has a " + p[0] + " attribute"
285: */
286: org.openlaszlo.i18n.LaszloMessages.getMessage(
287: ClassModel.class.getName(), "051018-214",
288: new Object[] { PLACEMENT_ATTR_NAME }));
289:
290: try {
291: // Replace this node by the class model.
292: NodeModel model = (NodeModel) nodeModel.clone();
293: // Set $classrootdepth on children of the class (but not the
294: // instance that it's applied to)
295: setChildrenClassRootDepth(model, 1);
296: model.updateMembers(instance);
297: model.setClassName(getSuperclassName());
298: return model;
299: } catch (CompilationError e) {
300: throw new InlineClassError(this , instance, e.getMessage());
301: }
302: }
303:
304: protected void setChildrenClassRootDepth(NodeModel model, int depth) {
305: final String CLASSROOTDEPTH_ATTRIBUTE_NAME = "$classrootdepth";
306: for (Iterator iter = model.getChildren().iterator(); iter
307: .hasNext();) {
308: NodeModel child = (NodeModel) iter.next();
309: // If it has already been set, this child is the result of
310: // a previous inline class expansion with a different
311: // classroot.
312: if (child.hasAttribute(CLASSROOTDEPTH_ATTRIBUTE_NAME))
313: continue;
314: child.setAttribute(CLASSROOTDEPTH_ATTRIBUTE_NAME,
315: new Integer(depth));
316: int childDepth = depth;
317: ClassModel childModel = child.getClassModel();
318: // If this is an undefined class, childModel will be null.
319: // This is an error, and other code signals a compiler
320: // warning. This test keeps it from resulting in a stack
321: // trace too.
322: if (childModel != null
323: && childModel.isSubclassOf(schema
324: .getClassModel("state")))
325: childDepth++;
326: setChildrenClassRootDepth(child, childDepth);
327: }
328: }
329:
330: /** Add an entry to the table of legally containable tags for a
331: * given tag */
332: public void addContainsElement(String childtag) {
333: mCanContainTags.add(childtag);
334: }
335:
336: public Set getContainsSet() {
337: return mCanContainTags;
338: }
339: }
340:
341: /**
342: * @copyright Copyright 2001-2007 Laszlo Systems, Inc. All Rights
343: * Reserved. Use is subject to license terms.
344: */
|