001: package uk.org.ponder.saxalizer;
002:
003: import java.util.ArrayList;
004: import java.util.HashMap;
005: import java.util.Iterator;
006: import java.util.List;
007: import java.util.Map;
008:
009: import org.xml.sax.AttributeList;
010: import org.xml.sax.HandlerBase;
011: import org.xml.sax.InputSource;
012: import org.xml.sax.Locator;
013: import org.xml.sax.SAXException;
014: import org.xml.sax.SAXParseException;
015:
016: import uk.org.ponder.arrayutil.ListUtil;
017: import uk.org.ponder.beanutil.PropertyAccessor;
018: import uk.org.ponder.conversion.StaticLeafParser;
019: import uk.org.ponder.reflect.ClassGetter;
020: import uk.org.ponder.reflect.ReflectUtils;
021: import uk.org.ponder.reflect.ReflectiveCache;
022: import uk.org.ponder.stringutil.CharWrap;
023: import uk.org.ponder.util.AssertionException;
024: import uk.org.ponder.util.CompletableDenumeration;
025: import uk.org.ponder.util.Denumeration;
026: import uk.org.ponder.util.EnumerationConverter;
027: import uk.org.ponder.util.Logger;
028: import uk.org.ponder.util.UniversalRuntimeException;
029:
030: /**
031: * The SAXalizer class is used to deserialize a tree of XML tags into a tree of
032: * Java objects. Please note that this class is over 5 years old and is due for
033: * a very major sandblasting - many comments are extremely out of date, in
034: * addition to its using the extremely obsolete SAX1 interface. See wiki for
035: * general comments on roadmap for this dinosaur.
036: * <p>
037: * Note from January '06 - this code is just getting worse and worse! More than
038: * anything it exemplifies the all-engulphing "ball of wax" pattern where poor
039: * design in one area simply proliferates and sucks more and more neighbouring
040: * infrastructure down with it. Will there EVER be time to fix it... or replace
041: * it with JiBX or comparable mature solution. Will the SAXalizer reach it's 6th
042: * birthday alive??!
043: * <p>
044: * Every class that wishes to be SAXalized must implement (at least) the
045: * interface SAXalizable, which allows the class to report the set methods it
046: * supports for attaching subobjects to itself that correspond to XML subtags.
047: * It may also support the interface SAXalizableAttrs in order to receive the
048: * attributes of the XML tag it corresponds to -- if it does not, any such
049: * attributes are thrown away.
050: *
051: * Note that the class of a SAXalizing object may also implement the
052: * DeSAXalizable interface. In this case, if an existing subobject is present
053: * and can be delivered through the interface, SAXalizing subobjects will be
054: * delivered onto the existing subobject rather than a new one created afresh.
055: *
056: * <p>
057: * Two "helper" base classes are called GenericSAXImpl, which allows a Java
058: * class to store arbitary XML subtags, and SAXalizableExtraAttrs, which allows
059: * it to store arbitrary XML attributes. Note that GenericSAXImpl itself
060: * implements SAXalizableExtraAttrs. With these two helper classes, the line is
061: * blurred between serializable Java objects and objects representing a DOM-like
062: * document structure. We can even have a class which uses both styles at once;
063: * implementing named methods for particular tags it is interested in, and
064: * storing the rest in a "pool" of GenericSAXImpl.
065: *
066: * <p>
067: * Note that a useful modification of the standard use of SAXAccessMethodSpec to
068: * specify a set method is to use "*" as the parameter for the set method
069: * argument type. This will instruct the SAXalizer to use the exact tag name of
070: * any subtags as the argument to the Class.forName() reflection method; the
071: * class thus referenced must a) exist, and b) be a subclass of the actual
072: * argument type of the set method specified.
073: *
074: * <p>
075: * At the bottom (err, top) of the XML tag tree we stop using the SAXalizable
076: * interface, since there will be no further subobjects to deliver. Instead, we
077: * use a scheme that externally encodes these object's leafiness so that we can
078: * efficiently use primitive Java types to represent them such as String and
079: * Date. A global registry class called SAXLeafParser stores a hashtable of
080: * Class objects to mechanisms which can parse that type from a String. If the
081: * SAXalizer discovers the argument type supplied through the SAXalizable
082: * interface for a set method is registered with the SAXLeafParser, it uses that
083: * mechanism for parsing any character data attached to the tag into a Java
084: * object rather than continuing creating SAXalizable objects.
085: *
086: * <p>
087: * Note that any character data arriving for non--leaf nodes is currently
088: * ignored. Should we ever do any true DOM-style parsing using the SAXalizer, a
089: * new interface type will be required to deliver it.
090: */
091:
092: public class SAXalizer extends HandlerBase {
093: private SAXalizerMappingContext mappingcontext;
094: private StaticLeafParser leafparser;
095:
096: public SAXalizer(SAXalizerMappingContext mappingcontext) {
097: this .mappingcontext = mappingcontext;
098: this .leafparser = mappingcontext.saxleafparser;
099: }
100:
101: /**
102: * Sets the EntityResolverStash this SAXalizer will use to resolve entities
103: * referred to by the XML document to be parsed.
104: *
105: * @param entityresolverstash
106: * The required EntityResolverStash object.
107: */
108:
109: public void setEntityResolverStash(
110: EntityResolverStash entityresolverstash) {
111: this .entityresolverstash = entityresolverstash;
112: }
113:
114: EntityResolverStash entityresolverstash;
115:
116: // The ParseContext class represents everything a DocumentHandler method
117: // needs to know about the Java object representing the currently parsing
118: // tag. The state of the SAXalizer consists of a stack of ParseContext
119: // objects representing the current path to the root XML tag.
120:
121: static class ParseContext {
122: // The object being constructed; this is created on startElement for
123: // SAXalizable-style XML peers, but only created immediately before
124: // calling the SAXalizable set method of the parent object in the case
125: // of a Leaf-style XML peer. For the leaf case, this object doubles as
126: // storage for the Class object for the leaf type.
127: Object object;
128: MethodAnalyser ma;
129: // The SET method in the parent that will be used to deliver this
130: // object when it is complete.
131: SAXAccessMethod parentsetter;
132: // where the parent is an array or collection, this is used to deliver
133: // multiple children. If it is simply an enumeration, a normal setMethod
134: // is called multiple times.
135: // QQQQQ economise on this at some point! One hashmap created per
136: // collection.
137: // TODO: We THOUGHT this could be put in the child object like "objectpeer",
138: // but then realised that it might have been deallocated as they were popped
139: HashMap denumerationmap;
140: // Is this object using the GenericSAXImpl object lookup scheme
141: boolean isgeneric;
142: // Is this object a leaf-style object
143: boolean isleaf;
144: // The character data seen so far
145: CharWrap textsofar;
146: // The key name if this is an attribute-keyed map entry
147: String mapkey;
148: // Currently only set for "mappable" types, but "in general" holds the
149: // object being constructed in the real tree.
150: Object objectpeer;
151:
152: ParseContext(Object object, MethodAnalyser ma,
153: boolean isgeneric, boolean isleaf,
154: SAXAccessMethod parentsetter) {
155: this .object = object;
156: this .ma = ma;
157: this .isgeneric = isgeneric;
158: this .isleaf = isleaf;
159: this .parentsetter = parentsetter;
160: textsofar = new CharWrap();
161: }
162:
163: public boolean hasDenumeration(String tag) {
164: if (denumerationmap == null) {
165: denumerationmap = new HashMap();
166: }
167: return (denumerationmap.get(tag) != null);
168: }
169:
170: public Denumeration getDenumeration(String tag) {
171: if (denumerationmap == null)
172: return null;
173: else
174: return (Denumeration) denumerationmap.get(tag);
175: }
176: }
177:
178: // A stack of ParseContexts
179: private List saxingobjects = new ArrayList();
180:
181: // Return the Saxing object at the top of the stack.
182: private ParseContext getSaxingObject() {
183: return (ParseContext) ListUtil.peek(saxingobjects);
184: }
185:
186: /**
187: * Blasts any parse-specific state held by this SAXalizer object, making it
188: * ready to start a fresh parse.
189: */
190: public void blastState() {
191: saxingobjects.clear();
192: }
193:
194: // Given a Class object, push a ParseContect object for that type onto the
195: // saxing objects stack. If the object is a leaf type (i.e. it has no
196: // subobjects and is
197: // registered with the leafparser), do not actually create the required object
198: // instance yet; instead, we store the leaf type CLASS itself!!!!
199: // An object is either SAXalizable or a leaf type.
200: private void pushObject(Class topush, Object oldinstance,
201: SAXAccessMethod parentsetter) {
202: if (topush.isPrimitive()) {
203: topush = StaticLeafParser.wrapClass(topush);
204: }
205: boolean isgeneric = GenericSAX.class.isAssignableFrom(topush);
206: boolean isleaf = leafparser.isLeafType(topush);
207: // parentsetter is null for the root object only.
208: boolean isdenumerable = parentsetter == null ? false
209: : parentsetter.isDenumerable();
210: ParseContext beingparsed = getSaxingObject();
211: // The creation of leaf objects is deferred until all their data has
212: // arrived.
213: Object newinstance = null;
214: ReflectiveCache reflectivecache = mappingcontext
215: .getReflectiveCache();
216: if (oldinstance == null || isdenumerable) {
217: if (isdenumerable
218: && oldinstance == null
219: && !beingparsed
220: .hasDenumeration(parentsetter.tagname)) {
221: // NB, do not try to make "old" instance (collection/"peer") if we are already
222: // in a denumeration, since it must be the array case.
223: oldinstance = ReflectUtils.instantiateContainer(
224: parentsetter.accessclazz,
225: ReflectUtils.UNKNOWN_SIZE, reflectivecache);
226: // Do NOT deliver the object to parent now for arrays, but wait until
227: // enclosing tag is complete in endElement.
228: if (!parentsetter.accessclazz.isArray()) {
229: parentsetter.setChildObject(beingparsed.object,
230: oldinstance);
231: }
232: }
233: newinstance = isleaf ? topush : reflectivecache
234: .construct(topush);
235: } else
236: newinstance = oldinstance;
237: MethodAnalyser ma = isleaf ? null : mappingcontext
238: .getAnalyser(newinstance.getClass());
239: // "reach into the past" and note that we are now within a denumeration.
240: // For denumerable types, oldinstance will be the previously obtained
241: // container class, and newinstance will be of the containee type.
242: if (isdenumerable) {
243: if (!beingparsed.hasDenumeration(parentsetter.tagname)) {
244: Denumeration den = EnumerationConverter
245: .getDenumeration(oldinstance, reflectivecache);
246: if (den == null) {
247: throw new UniversalRuntimeException(
248: "Child "
249: + oldinstance
250: + " in "
251: + topush
252: + " cannot be made denumerable via setter with tag name "
253: + parentsetter.tagname);
254: }
255: beingparsed.denumerationmap.put(parentsetter.tagname,
256: den);
257: }
258: }
259: saxingobjects.add(new ParseContext(newinstance, ma, isgeneric,
260: isleaf, parentsetter));
261: }
262:
263: // private CharWrap attributebuffer = new CharWrap();
264: // Try to send the attribute list to the object on top of the stack ---
265: // if it does not support the SAXalizableAttrs interface,
266: // the attributes will be simply thrown away.
267: private static void tryBlastAttrs(AttributeList attrlist,
268: SAXAccessMethodHash attrmethods, Object obj,
269: StaticLeafParser leafparser, boolean waspolymorphic,
270: boolean wasmap) throws SAXException {
271: SAXalizableExtraAttrs extraattrs = obj instanceof SAXalizableExtraAttrs ? (SAXalizableExtraAttrs) obj
272: : null;
273: boolean takesextras = extraattrs != null;
274: // use up each of the non-"extra" attributes one by one, and send any
275: // remaining
276: // ones into SAXalizableExtraAttrs
277: Map extras = takesextras ? extraattrs.getAttributes() : null;
278: boolean[] expended = takesextras ? new boolean[attrlist
279: .getLength()] : null;
280:
281: for (int i = 0; i < attrlist.getLength(); ++i) {
282: String attrname = attrlist.getName(i);
283: String attrvalue = attrlist.getValue(i);
284: if (attrname.equals(Constants.TYPE_ATTRIBUTE_NAME)
285: && waspolymorphic)
286: continue;
287: if (attrname.equals(Constants.KEY_ATTRIBUTE_NAME) && wasmap)
288: continue;
289: SAXAccessMethod setattrmethod = attrmethods.get(attrname);
290: if (setattrmethod != null) {
291: if (!setattrmethod.isdevnull) {
292: Object newchild = leafparser.parse(
293: setattrmethod.clazz, attrvalue);
294:
295: setattrmethod.setChildObject(obj, newchild); // invoke iiiiiit!
296: if (takesextras)
297: expended[i] = true;
298: }
299: } else if (takesextras) { // if not mapped, and it takes extras,
300: extras.put(attrname, attrvalue);
301: } else { // if all else fails, look for a default map member
302: // QQQQQ implement maps for main tags too.
303: SAXAccessMethod defaultattrmethod = attrmethods.get("");
304: if (defaultattrmethod == null) {
305: throw new UniversalRuntimeException(
306: "Couldn't locate handler for attribute "
307: + attrname + " in object "
308: + obj.getClass());
309: }
310: Object newchild = leafparser.parse(
311: defaultattrmethod.clazz, attrvalue);
312: Map defaultmap = EnumerationConverter
313: .getMap(defaultattrmethod.getChildObject(obj));
314: defaultmap.put(attrname, newchild);
315: }
316: } // end for each attribute presented by SAX
317:
318: } // end tryBlast Attrs
319:
320: SAXalizerCallback callback;
321:
322: /**
323: * Produce a subtree of objects from a SAX stream. This method produces a
324: * subtree of objects from a SAX stream, rooted at an object of the class
325: * specified, starting by interpreting the following SAX events as producing
326: * an object of the type specified. It is assumed that the
327: * <code>startElement</code> event for the root object that will be returned
328: * has just been seen on the stream, and that all further events in the SAX
329: * stream will be directed at this object until the matching
330: * <code>endElement</code> event.
331: *
332: * @param clazz
333: * Produce an object of this type.
334: * @param attrlist
335: * The attribute list that was attached to the
336: * <code>startElement</code> tag that we just saw.
337: * @param callback
338: * The caller of this method, who will receive the produced object on
339: * seeing of the <code>endElement</code> tag. At this point, he
340: * should stop forwarding <code>DocumentHandler</code> events to
341: * this object. This parameter may be <code>null</code>.
342: * @exception SAXException
343: * If an error occured while parsing the supplied input source.
344: */
345: public void produceSubtree(Object rootobj, AttributeList attrlist,
346: SAXalizerCallback callback) throws SAXException {
347: if (!saxingobjects.isEmpty()) {
348: throw new AssertionException(
349: "Attempt to produce new Subtree whilst another"
350: + " parse was in progress");
351: }
352: this .callback = callback;
353: pushObject(rootobj.getClass(), rootobj, null);
354: // DARN, which is the correct methodanalyser?
355: tryBlastAttrs(attrlist, getSaxingObject().ma.attrmethods,
356: rootobj, leafparser, false, false);
357: }
358:
359: /** ******* Begin methods for the DocumentHandler interface ******* */
360:
361: public InputSource resolveEntity(String publicID, String systemID) {
362: Logger.println("SAXalizer was asked to resolve public ID "
363: + publicID + " systemID " + systemID,
364: Logger.DEBUG_INFORMATIONAL);
365: return entityresolverstash == null ? null : entityresolverstash
366: .resolve(publicID);
367: }
368:
369: private Locator locator;
370:
371: public void setDocumentLocator(Locator locator) {
372: // System.out.println("Locator received");
373: this .locator = locator;
374: }
375:
376: public static String renderLocator(Locator torender) {
377: return "line " + torender.getLineNumber() + " column "
378: + torender.getColumnNumber();
379: }
380:
381: /**
382: * Implements the DocumentHandler interface.
383: *
384: * @param tagname
385: * The tag name for the element just seen in the SAX stream.
386: * @param attrlist
387: * The attribute list of the tag just seen in the SAX stream.
388: * @exception SAXException
389: * If any exception requires to be propagated from this
390: * interface.
391: */
392: public void startElement(String tagname, AttributeList attrlist)
393: throws SAXException {
394: try {
395: // an element has started, and we must start to construct an object to put
396: // it in.
397: // The object on top of the ParseContext stack represents the parent object
398: // of this object.
399: ParseContext beingparsed = getSaxingObject();
400: if (beingparsed.isleaf) {
401: throw new SAXParseException("Received open tag "
402: + tagname + " for leaf tag "
403: + beingparsed.parentsetter.tagname, locator);
404: }
405: SAXAccessMethodHash tagmethods = beingparsed.ma.tagmethods;
406: // Firstly we will look into its AccessMethodHash to see if the tagname we
407: // have just seen
408: // has been registered by the class of the parent object.
409: SAXAccessMethod am = tagmethods.get(tagname);
410:
411: Class newobjclass = null; // for some reason, idiot compiler cannot
412: // analyse that this is set
413: if (am == null) {
414: // if we failed to find a registered method for this tag name
415: try { // attempt to look up the class name now so that the forthcoming
416: // if statement can be nicely ordered
417: if (tagname.indexOf(':') == -1
418: && tagname.indexOf('.') != -1)
419: newobjclass = Class.forName(tagname);
420: } catch (Exception e) {
421: } // exception simply indicates that the tag name is not a class
422: if (tagmethods.get("*") != null && newobjclass != null) {
423: // if the parent is polymorphic, and class name lookup succeeded, do
424: // nothing.
425: // Note, this is REALLY blank! newobjtype will be set in the try
426: // above.
427: } else if (beingparsed.object instanceof GenericSAX) {
428: // but the parent is generic
429: newobjclass = GenericSAXImpl.class; // Child of generic will always be
430: // generic.
431: } else { // the parent is not generic
432: throw new SAXParseException(
433: "Unexpected tag '"
434: + tagname
435: + "' found while parsing child of"
436: + (beingparsed.object == null ? " null object"
437: : " object of "
438: + beingparsed.object
439: .getClass()),
440: locator);
441: }
442: } // end if no registered method
443: else {
444: String typeattrname = Constants.TYPE_ATTRIBUTE_NAME;
445: // QQQQQ genericise this somehow.
446: String typeattrvalue = attrlist.getValue(typeattrname);
447: if (am.ispolymorphic && typeattrvalue != null) {
448: newobjclass = mappingcontext.classnamemanager
449: .findClazz(typeattrvalue);
450: if (newobjclass == null) {
451: newobjclass = ClassGetter
452: .forName(typeattrvalue);
453: }
454: if (newobjclass == null) {
455: throw new SAXParseException(
456: "Polymorphic tag "
457: + tagname
458: + " has \"type\" attribute with value "
459: + typeattrvalue
460: + " which cannot be resolved to a class ",
461: locator);
462: }
463: } else {
464: newobjclass = am.clazz;
465: }
466: }
467: if (Logger.passDebugLevel(Logger.DEBUG_EXTRA_INFO)) {
468: Logger.println("ELEMENT CLASS determined to be "
469: + newobjclass);
470: }
471:
472: // if the parent is DeSAXalizable and we can find a unique non-null object
473: // already present at this position, use it rather than creating a new
474: // one.
475: // do not do this for leaves, since they will be replaced anyway, and in
476: // any
477: // case the existing object represents the leaf's class.
478: Object oldobj = null;
479: // enumerations and non-getters are out - we could never write to them.
480: // if it is a leaf type it is out, UNLESS it is a multiple in which case
481: // it
482: // is denumerable. It is ALSO out if it is "exact" since the class author
483: // presumably has provided a precise "add" method he wants us to use.
484: if (am != null
485: && am.canGet()
486: && !am.isenumonly
487: && (am.ismultiple || !leafparser
488: .isLeafType(am.clazz)) && !am.isexactsetter) {
489: oldobj = am.getChildObject(beingparsed.object);
490: if (oldobj != null) {
491: // Logger.println("Acquired old object " + oldobj + " from parent "
492: // + beingparsed.object + " of class " + am.clazz,
493: // Logger.DEBUG_EXTRA_INFO);
494: }
495: }
496: pushObject(newobjclass, oldobj, am);
497:
498: ParseContext newcontext = getSaxingObject();
499: if (am != null && am.ismappable) {
500: newcontext.mapkey = attrlist
501: .getValue(Constants.KEY_ATTRIBUTE_NAME);
502: newcontext.objectpeer = am
503: .getChildObject(beingparsed.object);
504: }
505: if (!newcontext.isleaf) {
506: tryBlastAttrs(attrlist, newcontext.ma.attrmethods,
507: newcontext.object, leafparser,
508: am == null ? false : am.ispolymorphic,
509: am == null ? false : am.ismappable);
510: }
511: } catch (Exception e) {
512: if (e instanceof SAXParseException) {
513: throw ((SAXParseException) e);
514: } else {
515: throw UniversalRuntimeException.accumulate(e,
516: "Error parsing at " + renderLocator(locator));
517: }
518: }
519: }
520:
521: /**
522: * Implements the DocumentHandler interface.
523: *
524: * @param ch
525: * An array holding the character data seen in the SAX stream.
526: * @param start
527: * The index of the character data within the supplied array.
528: * @param length
529: * The length of the character data within the supplied array.
530: * @exception SAXException
531: * If any exception requires to be propagated from this
532: * interface.
533: */
534:
535: public void characters(char[] ch, int start, int length)
536: throws SAXException {
537: if (saxingobjects.isEmpty()) {
538: throw new SAXParseException("Unexpected character data "
539: + new String(ch, start, length)
540: + " seen when there"
541: + " was no active object being parsed", locator);
542: }
543: ParseContext beingparsed = getSaxingObject();
544: beingparsed.textsofar.append(ch, start, length);
545: // System.err.println("CHARACTERS received at SAXALIZER:
546: // "+beingparsed.textsofar);
547: }
548:
549: /**
550: * Implements the DocumentHandler interface.
551: *
552: * @param tagname
553: * The tag name for the element just closed in the SAX stream.
554: * @exception SAXException
555: * If any exception requires to be propagated from this
556: * interface.
557: */
558:
559: public void endElement(String tagname) throws SAXException {
560: // Logger.println("END ELEMENT received to SAXALIZER: " + tagname,
561: // Logger.DEBUG_EXTRA_INFO);
562: if (saxingobjects.isEmpty()) {
563: throw new SAXParseException(
564: "Unexpected closing tag for "
565: + tagname
566: + " seen when there was no active object being parsed",
567: locator);
568: }
569: // Our task is now to deliver the object to its parent. Firstly take
570: // care of three special cases before finishing up.
571: ParseContext beingparsed = getSaxingObject();
572: SAXAccessMethod bodymethod = beingparsed.ma == null ? null
573: : beingparsed.ma.bodymethod;
574:
575: AccessMethod parentsetter = beingparsed.parentsetter;
576: // Test the special cases first, i) leaf node
577: if (beingparsed.isleaf) {
578: // Leaf node objects are only created at this point
579: beingparsed.object = leafparser.parse(
580: (Class) beingparsed.object, beingparsed.textsofar
581: .toString());
582: } else if (beingparsed.isgeneric) {
583: // special case ii) generic parent - grab the intermediate text
584: // System.out.println("About to parse generic");
585: GenericSAX object = (GenericSAX) beingparsed.object;
586: object.setData(beingparsed.textsofar.toString());
587: object.setTag(tagname);
588: } else if (bodymethod != null) {
589: // special case iii) A body method is registered to receive text
590: String body = beingparsed.textsofar.toString();
591: Object newchild = leafparser.parse(bodymethod.clazz, body);
592: bodymethod.setChildObject(beingparsed.object, newchild);
593: }
594:
595: // deal with "just destroyed" completable denumerations (array types)
596: if (beingparsed.denumerationmap != null
597: && !beingparsed.denumerationmap.isEmpty()) {
598: for (Iterator denit = beingparsed.denumerationmap.keySet()
599: .iterator(); denit.hasNext();) {
600: String denkey = (String) denit.next();
601: Object denval = beingparsed.denumerationmap.get(denkey);
602: if (denval instanceof CompletableDenumeration) {
603: Object completed = ((CompletableDenumeration) denval)
604: .complete();
605: AccessMethod deliver = beingparsed.ma
606: .getAccessMethod(denkey);
607: deliver.setChildObject(beingparsed.object,
608: completed);
609: }
610: }
611: }
612:
613: ListUtil.pop(saxingobjects); // remove the completed object from the stack.
614:
615: // Now we must try to deliver the completed object to the parent object.
616: // if nothing left on the stack, provide the root object to our caller and
617: // return.
618: if (saxingobjects.isEmpty()) {
619: if (callback != null)
620: callback.productionComplete(beingparsed.object);
621: return;
622: }
623:
624: ParseContext parentcontext = getSaxingObject();
625:
626: // Logger.println("SAXing object is " + parentcontext.object,
627: // Logger.DEBUG_SUBATOMIC);
628:
629: Object parentobject = parentcontext.object;
630:
631: // Now deal with CURRENT denumerations for the just closed tag
632: Denumeration den = null;
633: if (parentcontext.isgeneric) {
634: ((GenericSAX) parentobject)
635: .addChild((GenericSAX) beingparsed.object);
636: } else if ((den = parentcontext.getDenumeration(tagname)) != null) {
637: den.add(beingparsed.object);
638: } else if (beingparsed.parentsetter.ismappable
639: && !beingparsed.parentsetter.isexactsetter) {
640: if (beingparsed.mapkey == null) {
641: throw new SAXParseException("Mappable type for tag "
642: + tagname + " did not supply a map key",
643: locator);
644: }
645: PropertyAccessor pa = MethodAnalyser.getPropertyAccessor(
646: beingparsed.objectpeer, mappingcontext);
647: pa.setProperty(beingparsed.objectpeer, beingparsed.mapkey,
648: beingparsed.object);
649: } else {
650: parentsetter.setChildObject(parentobject,
651: beingparsed.object);
652: }
653:
654: // else if we found no set method, and parent was generic, deliver the //
655: //object by GenericSax interface.
656:
657: } // end method endElement
658: }
|