KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > xb > binding > sunday > unmarshalling > impl > runtime > RtElementHandler


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.xb.binding.sunday.unmarshalling.impl.runtime;
23
24 import java.lang.reflect.Array JavaDoc;
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.lang.reflect.Method JavaDoc;
27 import java.lang.reflect.Modifier JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Map JavaDoc;
30 import javax.xml.namespace.NamespaceContext JavaDoc;
31 import javax.xml.namespace.QName JavaDoc;
32 import org.jboss.logging.Logger;
33 import org.jboss.util.Classes;
34 import org.jboss.xb.binding.Constants;
35 import org.jboss.xb.binding.GenericValueContainer;
36 import org.jboss.xb.binding.JBossXBRuntimeException;
37 import org.jboss.xb.binding.SimpleTypeBindings;
38 import org.jboss.xb.binding.Util;
39 import org.jboss.xb.binding.introspection.FieldInfo;
40 import org.jboss.xb.binding.group.ValueList;
41 import org.jboss.xb.binding.group.ValueListHandler;
42 import org.jboss.xb.binding.group.ValueListInitializer;
43 import org.jboss.xb.binding.metadata.AddMethodMetaData;
44 import org.jboss.xb.binding.metadata.ClassMetaData;
45 import org.jboss.xb.binding.metadata.MapEntryMetaData;
46 import org.jboss.xb.binding.metadata.PackageMetaData;
47 import org.jboss.xb.binding.metadata.PropertyMetaData;
48 import org.jboss.xb.binding.metadata.PutMethodMetaData;
49 import org.jboss.xb.binding.metadata.ValueMetaData;
50 import org.jboss.xb.binding.sunday.unmarshalling.AttributeBinding;
51 import org.jboss.xb.binding.sunday.unmarshalling.AttributeHandler;
52 import org.jboss.xb.binding.sunday.unmarshalling.CharactersHandler;
53 import org.jboss.xb.binding.sunday.unmarshalling.ElementBinding;
54 import org.jboss.xb.binding.sunday.unmarshalling.ModelGroupBinding;
55 import org.jboss.xb.binding.sunday.unmarshalling.ParticleBinding;
56 import org.jboss.xb.binding.sunday.unmarshalling.ParticleHandler;
57 import org.jboss.xb.binding.sunday.unmarshalling.SchemaBinding;
58 import org.jboss.xb.binding.sunday.unmarshalling.TermBinding;
59 import org.jboss.xb.binding.sunday.unmarshalling.TypeBinding;
60 import org.jboss.xb.binding.sunday.unmarshalling.WildcardBinding;
61 import org.xml.sax.Attributes JavaDoc;
62
63 /**
64  * @author <a HREF="mailto:alex@jboss.org">Alexey Loubyansky</a>
65  * @version <tt>$Revision: 2136 $</tt>
66  */

67 public class RtElementHandler
68    implements ParticleHandler
69 {
70    private static final Logger log = Logger.getLogger(RtElementHandler.class);
71
72    public static final RtElementHandler INSTANCE = new RtElementHandler();
73    
74    // ParticleHandler impl
75

76    /**
77     * TODO: it seems like for correct type resolution in startParticle
78     * I should take into account the way the object is going to be added
79     * to the parent in setParent (and, hence, do some steps that are done in setParticle).
80     * In setParent then I should reuse the results of what has been done in startParticle.
81     */

82    public Object JavaDoc startParticle(Object JavaDoc parent,
83                                QName JavaDoc elementName,
84                                ParticleBinding particle,
85                                Attributes JavaDoc attrs,
86                                NamespaceContext JavaDoc nsCtx)
87    {
88       TermBinding term = particle.getTerm();
89       Object JavaDoc o = startElement(parent, elementName, particle);
90       if(!term.isModelGroup())
91       {
92          ElementBinding element = (ElementBinding)term;
93          if(o != null)
94          {
95             attrs = element.getType().expandWithDefaultAttributes(attrs);
96             attributes(o, elementName, element, attrs, nsCtx);
97          }
98       }
99       return o;
100    }
101
102    public void setParent(Object JavaDoc parent,
103                          Object JavaDoc o,
104                          QName JavaDoc qName,
105                          ParticleBinding particle,
106                          ParticleBinding parentParticle)
107    {
108       TermBinding term = particle.getTerm();
109       if(term.isSkip())
110       {
111          return;
112       }
113
114       boolean trace = log.isTraceEnabled();
115       if(trace)
116       {
117          log.trace("setParent " + qName + " parent=" + parent + " object=" + o + " term=" + term);
118       }
119
120       TermBinding parentTerm = parentParticle.getTerm();
121
122       if(term.isMapEntryKey())
123       {
124          if(trace)
125          {
126             log.trace("setParent " + qName + " mapKey");
127          }
128
129          if(parent instanceof MapEntry)
130          {
131             MapEntry mapEntry = (MapEntry)parent;
132             mapEntry.setKey(o);
133          }
134          else if(parentTerm != null)
135          {
136             MapEntryMetaData mapEntryMetaData = getMapEntryMetaData(parentTerm, qName);
137
138             String JavaDoc getKeyMethodName = mapEntryMetaData.getGetKeyMethod();
139             if(getKeyMethodName == null)
140             {
141                getKeyMethodName = "getKey";
142             }
143
144             String JavaDoc setKeyMethodName = mapEntryMetaData.getSetKeyMethod();
145             if(setKeyMethodName == null)
146             {
147                setKeyMethodName = "setKey";
148             }
149
150             Class JavaDoc parentCls = parent.getClass();
151             Method JavaDoc setKeyMethod = getSetMethod(parentCls, getKeyMethodName, setKeyMethodName);
152             invokeSetter(setKeyMethod, parent, o, setKeyMethodName);
153          }
154          else
155          {
156             throw new JBossXBRuntimeException(
157                "Element " +
158                qName +
159                " bound as map entry key but parent element is not recognized as map entry and its metadata is not available."
160             );
161          }
162       }
163       else if(term.isMapEntryValue())
164       {
165          if(trace)
166          {
167             log.trace("setParent " + qName + " mapValue");
168          }
169
170          if(parent instanceof MapEntry)
171          {
172             MapEntry mapEntry = (MapEntry)parent;
173             mapEntry.setValue(o);
174          }
175          else if(parentTerm != null)
176          {
177             MapEntryMetaData mapEntryMetaData = getMapEntryMetaData(parentTerm, qName);
178             setMapEntryValue(mapEntryMetaData, parent, o);
179          }
180          else
181          {
182             throw new JBossXBRuntimeException(
183                "Element " +
184                qName +
185                " bound as map entry key but parent element is not recognized as map entry and its metadata is not available."
186             );
187          }
188       }
189       else
190       {
191          Object JavaDoc owner = parent;
192          if(parent instanceof MapEntry)
193          {
194             if(trace)
195             {
196                log.trace("setParent " + qName + " mapEntry");
197             }
198
199             MapEntry mapEntry = (MapEntry)parent;
200             owner = mapEntry.getValue();
201             if(owner == null)
202             {
203                if(parentTerm == null)
204                {
205                   throw new JBossXBRuntimeException("Binding metadata needed for lazy map entry value instantiation is not available " +
206                      "for parent element of element " +
207                      qName
208                   );
209                }
210
211                MapEntryMetaData mapEntryMetaData = getMapEntryMetaData(parentTerm, qName);
212                String JavaDoc valueType = mapEntryMetaData.getValueType();
213                if(valueType == null)
214                {
215                   throw new JBossXBRuntimeException("Element " +
216                      qName +
217                      " is supposed to be bound as map entry value with lazy value instantiation " +
218                      "but value type is not specified in its map entry metadata."
219                   );
220                }
221
222                Class JavaDoc valueCls;
223                try
224                {
225                   valueCls = Thread.currentThread().getContextClassLoader().loadClass(valueType);
226                }
227                catch(ClassNotFoundException JavaDoc e)
228                {
229                   throw new JBossXBRuntimeException(
230                      "Failed to load value type specified in the map entry metadata: " + valueType
231                   );
232                }
233
234                try
235                {
236                   owner = valueCls.newInstance();
237                }
238                catch(Exception JavaDoc e)
239                {
240                   throw new JBossXBRuntimeException(
241                      "Failed to create an instance of value type " + valueType + ": " + e.getMessage()
242                   );
243                }
244
245                setMapEntryValue(mapEntryMetaData, parent, owner);
246             }
247          }
248
249          // the wildcard this element is a content of
250
WildcardBinding wildcard = null;
251          if(parentTerm != null && !parentTerm.isModelGroup())
252          {
253             ElementBinding parentElement = (ElementBinding)parentTerm;
254             TypeBinding parentType = parentElement.getType();
255             wildcard = parentType.getWildcard();
256             // there should be a better way of checking this
257
if(wildcard != null && parentType.getElement(qName) != null)
258             {
259                wildcard = null;
260             }
261          }
262
263          if(tryPut(owner, o, qName, term, trace))
264          {
265          }
266          else if(tryAdd(owner, o, qName, term, wildcard, trace))
267          {
268          }
269          else if (owner instanceof GenericValueContainer)
270          {
271             if (trace)
272             {
273                log.trace("setParent " + qName + " addChild");
274             }
275             ((GenericValueContainer) owner).addChild(qName, o);
276          }
277          else if (owner instanceof Collection JavaDoc)
278          {
279             if (trace)
280             {
281                log.trace("setParent " + qName + " collection.add()");
282             }
283             ((Collection JavaDoc) owner).add(o);
284          }
285          else
286          {
287             PropertyMetaData propertyMetaData = wildcard == null ? null : wildcard.getPropertyMetaData();
288             if (propertyMetaData == null)
289             {
290                propertyMetaData = term.getPropertyMetaData();
291             }
292
293             String JavaDoc propName = null;
294             String JavaDoc colType = null;
295             if (propertyMetaData != null)
296             {
297                propName = propertyMetaData.getName();
298                colType = propertyMetaData.getCollectionType();
299             }
300
301             if (propName == null)
302             {
303                propName = Util.xmlNameToFieldName(qName.getLocalPart(), term.getSchema().isIgnoreLowLine());
304             }
305
306             if (trace)
307             {
308                log.trace("setParent " + qName + " metadata set " + propName);
309             }
310
311             /*if(particle.isRepeatable())
312             {
313                RtUtil.add(owner, o, propName, colType,
314                   term.getSchema().isIgnoreUnresolvedFieldOrClass(),
315                   term.getValueAdapter()
316                );
317             }
318             else
319             {*/

320                RtUtil.set(owner, o, propName, colType,
321                      term.getSchema().isIgnoreUnresolvedFieldOrClass(),
322                      term.getValueAdapter());
323             //}
324
}
325       }
326    }
327
328    public Object JavaDoc endParticle(Object JavaDoc o, QName JavaDoc elementName, ParticleBinding particle)
329    {
330       TermBinding term = particle.getTerm();
331       if(term.isSkip())
332       {
333          return o;
334       }
335
336       boolean trace = log.isTraceEnabled();
337       if(trace)
338       {
339          log.trace("endParticle " + elementName + " object=" + o + " term=" + term);
340       }
341
342       if(o instanceof GenericValueContainer)
343       {
344          try
345          {
346             if(trace)
347             {
348                log.trace("endParticle " + elementName + " instantiate()");
349             }
350             o = ((GenericValueContainer)o).instantiate();
351          }
352          catch(JBossXBRuntimeException e)
353          {
354             throw e;
355          }
356          catch(RuntimeException JavaDoc e)
357          {
358             throw new JBossXBRuntimeException("Container failed to create an instance for " +
359                elementName +
360                ": " + e.getMessage(), e
361             );
362          }
363       }
364
365       return o;
366    }
367
368    // Private
369

370    private Object JavaDoc startElement(Object JavaDoc parent, QName JavaDoc elementName, ParticleBinding particle)
371    {
372       TermBinding term = particle.getTerm();
373       if(term.isSkip())
374       {
375          return parent;
376       }
377
378       boolean trace = log.isTraceEnabled();
379       if(trace)
380       {
381          log.trace("startElement " + elementName + " parent=" + parent + " term=" + term);
382       }
383
384       ClassMetaData classMetaData = term.getClassMetaData();
385       MapEntryMetaData mapEntryMetaData = term.getMapEntryMetaData();
386
387       if(!term.isModelGroup())
388       {
389          TypeBinding type = ((ElementBinding)term).getType();
390          if(!type.isStartElementCreatesObject() ||
391             classMetaData == null && mapEntryMetaData == null && Constants.QNAME_ANYTYPE.equals(type.getQName()))
392          {
393             if(trace)
394             {
395                log.trace("startElement " + elementName + " does not create an object");
396             }
397             return null;
398          }
399       }
400
401       Object JavaDoc o = null;
402
403       // if addMethod is specified, it's probably some collection field
404
// but should not be set as a property. Instead, items are added to it using the addMethod
405
ElementBinding arrayItem = null;
406       if(!term.isModelGroup())
407       {
408          TypeBinding type = ((ElementBinding)term).getType();
409          if(type.getAttributes().isEmpty())
410          {
411             ParticleBinding typeParticle = type.getParticle();
412             ModelGroupBinding modelGroup = (ModelGroupBinding)(typeParticle == null ? null : typeParticle.getTerm());
413             arrayItem = modelGroup == null ? null : modelGroup.getArrayItem();
414
415             // todo refactor later (move it to modelGroup.getArrayItem()?)
416
if(arrayItem != null &&
417                (arrayItem.isSkip() ||
418                arrayItem.getMapEntryMetaData() != null ||
419                arrayItem.getPutMethodMetaData() != null ||
420                arrayItem.getAddMethodMetaData() != null
421                ))
422             {
423                arrayItem = null;
424             }
425          }
426       }
427
428       if(arrayItem != null)
429       {
430          Class JavaDoc wrapperType = null;
431          if(classMetaData != null)
432          {
433             wrapperType = loadClassForTerm(classMetaData.getImpl(),
434                term.getSchema().isIgnoreUnresolvedFieldOrClass(),
435                elementName
436             );
437
438             if(GenericValueContainer.class.isAssignableFrom(wrapperType) ||
439                Collection JavaDoc.class.isAssignableFrom(wrapperType) ||
440                Map JavaDoc.class.isAssignableFrom(wrapperType))
441             {
442                return newInstance(wrapperType, elementName, term.getSchema().isUseNoArgCtorIfFound());
443             }
444          }
445
446          if(wrapperType == null && parent == null)
447          {
448             Class JavaDoc itemType = classForElement(arrayItem, null);
449             if(itemType != null)
450             {
451                if(trace)
452                {
453                   log.trace("startElement " + elementName + " new array " + itemType.getName());
454                }
455                o = GenericValueContainer.FACTORY.array(itemType);
456             }
457          }
458          else
459          {
460             PropertyMetaData propertyMetaData = wrapperType == null ?
461                term.getPropertyMetaData() : arrayItem.getPropertyMetaData();
462
463             String JavaDoc propName;
464             if(propertyMetaData == null)
465             {
466                propName = Util.xmlNameToFieldName(
467                   wrapperType == null ? elementName.getLocalPart() : arrayItem.getQName().getLocalPart(),
468                   term.getSchema().isIgnoreLowLine()
469                );
470             }
471             else
472             {
473                propName = propertyMetaData.getName();
474             }
475
476             if(trace)
477             {
478                log.trace("startElement " + elementName + " property=" + propName);
479             }
480
481             Class JavaDoc parentClass = wrapperType;
482             if(wrapperType == null)
483             {
484                if(parent instanceof GenericValueContainer)
485                {
486                   parentClass = ((GenericValueContainer)parent).getTargetClass();
487                }
488                else if(parent instanceof ValueList)
489                {
490                   parentClass = ((ValueList)parent).getTargetClass();
491                }
492                else
493                {
494                   parentClass = parent.getClass();
495                }
496             }
497
498             Class JavaDoc fieldType;
499             if(parentClass.isArray())
500             {
501                fieldType = parentClass.getComponentType();
502             }
503             else
504             {
505                fieldType = FieldInfo.getFieldInfo(parentClass, propName, true).getType();
506                if(particle.isRepeatable() && fieldType.isArray())
507                {
508                   fieldType = fieldType.getComponentType();
509                }
510             }
511
512             if(fieldType.isArray())
513             {
514                o = GenericValueContainer.FACTORY.array(wrapperType, propName, fieldType.getComponentType());
515             }
516             else if(Collection JavaDoc.class.isAssignableFrom(fieldType))
517             {
518                //System.out.println("GeenericValueContainer.child: " + elementName);
519
o = new ValueListInitializer().newValueList(ValueListHandler.FACTORY.child(), Collection JavaDoc.class);
520                //o = new ArrayList();
521
}
522             else
523             {
524                o = GenericValueContainer.FACTORY.array(wrapperType, propName, fieldType);
525             }
526          }
527       }
528       else
529       {
530          if(mapEntryMetaData != null)
531          {
532             if(mapEntryMetaData.getImpl() != null)
533             {
534                Class JavaDoc cls = loadClassForTerm(mapEntryMetaData.getImpl(),
535                   term.getSchema().isIgnoreUnresolvedFieldOrClass(),
536                   elementName
537                );
538
539                if(trace)
540                {
541                   log.trace("startElement " + elementName + " new map entry " + cls.getName());
542                }
543
544                o = newInstance(cls, elementName, term.getSchema().isUseNoArgCtorIfFound());
545             }
546             else
547             {
548                o = new MapEntry();
549                if(trace)
550                {
551                   log.trace("startElement " + elementName + " new map entry");
552                }
553             }
554
555             if(mapEntryMetaData.isNonNullValue() && mapEntryMetaData.getValueType() != null)
556             {
557                Class JavaDoc mapValueType;
558                try
559                {
560                   mapValueType =
561                      Thread.currentThread().getContextClassLoader().loadClass(mapEntryMetaData.getValueType());
562                }
563                catch(ClassNotFoundException JavaDoc e)
564                {
565                   throw new JBossXBRuntimeException("startElement failed for " +
566                      elementName +
567                      ": failed to load class " +
568                      mapEntryMetaData.getValueType() +
569                      " for map entry value."
570                   );
571                }
572
573                Object JavaDoc value;
574                try
575                {
576                   if(trace)
577                   {
578                      log.trace("startElement " + elementName + " map value type " + mapEntryMetaData.getValueType());
579                   }
580                   value = mapValueType.newInstance();
581                }
582                catch(Exception JavaDoc e)
583                {
584                   throw new JBossXBRuntimeException("startElement failed for " +
585                      elementName +
586                      ": failed to create an instance of " +
587                      mapValueType +
588                      " for map entry value."
589                   );
590                }
591
592                if(o instanceof MapEntry)
593                {
594                   ((MapEntry)o).setValue(value);
595                }
596                else
597                {
598                   String JavaDoc getValueMethodName = mapEntryMetaData.getGetValueMethod();
599                   if(getValueMethodName == null)
600                   {
601                      getValueMethodName = "getValue";
602                   }
603
604                   String JavaDoc setValueMethodName = mapEntryMetaData.getSetValueMethod();
605                   if(setValueMethodName == null)
606                   {
607                      setValueMethodName = "setValue";
608                   }
609
610                   Method JavaDoc getValueMethod;
611                   try
612                   {
613                      getValueMethod = o.getClass().getMethod(getValueMethodName, null);
614                   }
615                   catch(NoSuchMethodException JavaDoc e)
616                   {
617                      throw new JBossXBRuntimeException("getValueMethod=" +
618                         getValueMethodName +
619                         " is not found in map entry " + o.getClass()
620                      );
621                   }
622
623                   Method JavaDoc setValueMethod;
624                   try
625                   {
626                      setValueMethod =
627                         o.getClass().getMethod(setValueMethodName, new Class JavaDoc[]{getValueMethod.getReturnType()});
628                   }
629                   catch(NoSuchMethodException JavaDoc e)
630                   {
631                      throw new JBossXBRuntimeException("setValueMethod=" +
632                         setValueMethodName +
633                         "(" +
634                         getValueMethod.getReturnType().getName() +
635                         " value) is not found in map entry " + o.getClass()
636                      );
637                   }
638
639                   try
640                   {
641                      setValueMethod.invoke(o, new Object JavaDoc[]{value});
642                   }
643                   catch(Exception JavaDoc e)
644                   {
645                      throw new JBossXBRuntimeException("setValueMethod=" +
646                         setValueMethodName +
647                         " failed: owner=" +
648                         o +
649                         ", value=" + value + ", msg=" + e.getMessage(), e
650                      );
651                   }
652                }
653             }
654          }
655          else
656          {
657             // todo: for now we require metadata for model groups to be bound
658
// todo 2: parent.getClass() is not going to work for containers
659
Class JavaDoc parentClass = null;
660             if(parent != null)
661             {
662                if(parent instanceof GenericValueContainer)
663                {
664                   parentClass = ((GenericValueContainer)parent).getTargetClass();
665                }
666                else if(parent instanceof ValueList)
667                {
668                   parentClass = ((ValueList)parent).getTargetClass();
669                }
670                else
671                {
672                   parentClass = parent.getClass();
673                }
674             }
675
676             Class JavaDoc cls;
677             if(term.isModelGroup())
678             {
679                if(classMetaData == null)
680                {
681                   throw new JBossXBRuntimeException(
682                      "Model groups should be annotated with 'class' annotation to be bound."
683                   );
684                }
685                cls = loadClassForTerm(classMetaData.getImpl(),
686                   term.getSchema().isIgnoreUnresolvedFieldOrClass(),
687                   elementName
688                );
689             }
690             else
691             {
692                ElementBinding element = (ElementBinding)term;
693                cls = classForNonArrayItem(element, parentClass);
694                if(cls != null)
695                {
696                   // todo: before that, the type should be checked for required attributes and elements
697
TypeBinding simpleType = element.getType().getSimpleType();
698                   if(simpleType != null)
699                   {
700                      Class JavaDoc simpleCls = classForSimpleType(simpleType, element.isNillable());
701                      if(cls.equals(simpleCls) ||
702                         cls.isPrimitive() && Classes.getPrimitiveWrapper(cls) == simpleCls ||
703                         simpleCls.isPrimitive() && Classes.getPrimitiveWrapper(simpleCls) == cls)
704                      {
705                         cls = null;
706                      }
707                   }
708                }
709             }
710
711             if(cls != null)
712             {
713                boolean noArgCtor;
714                if(classMetaData == null)
715                {
716                   noArgCtor = term.getSchema().isUseNoArgCtorIfFound();
717                }
718                else
719                {
720                   Boolean JavaDoc termUsesNoArgCtor = classMetaData.isUseNoArgCtor();
721                   noArgCtor = termUsesNoArgCtor == null ?
722                      term.getSchema().isUseNoArgCtorIfFound() : termUsesNoArgCtor.booleanValue(); }
723
724                if(trace)
725                {
726                   log.trace("startElement " + elementName + " new " + cls.getName() + ", noArgCtor=" + noArgCtor);
727                }
728                o = newInstance(cls, elementName, noArgCtor);
729             }
730          }
731       }
732       return o;
733    }
734
735    private void attributes(Object JavaDoc o,
736                            QName JavaDoc elementName,
737                            ElementBinding element,
738                            Attributes JavaDoc attrs,
739                            NamespaceContext JavaDoc nsCtx)
740    {
741       TypeBinding type = element.getType();
742       for(int i = 0; i < attrs.getLength(); ++i)
743       {
744          QName JavaDoc attrName = new QName JavaDoc(attrs.getURI(i), attrs.getLocalName(i));
745          AttributeBinding binding = type.getAttribute(attrName);
746          if(binding != null)
747          {
748             AttributeHandler handler = binding.getHandler();
749             if(handler != null)
750             {
751                Object JavaDoc value = handler.unmarshal(elementName, attrName, binding, nsCtx, attrs.getValue(i));
752                handler.attribute(elementName, attrName, binding, o, value);
753             }
754             else
755             {
756                throw new JBossXBRuntimeException(
757                   "Attribute binding present but has no handler: element=" + elementName + ", attrinute=" + attrName
758                );
759             }
760          }
761          else
762          {
763             if(!Constants.NS_XML_SCHEMA_INSTANCE.equals(attrs.getURI(i)))
764             {
765                CharactersHandler simpleType = type.getCharactersHandler();
766                Object JavaDoc value;
767                if(simpleType == null)
768                {
769                   value = attrs.getValue(i);
770                   RtUtil.set(o, attrName, value, element.getSchema().isIgnoreLowLine());
771                }
772             }
773          }
774       }
775    }
776
777    private boolean tryAdd(Object JavaDoc owner,
778                           Object JavaDoc o,
779                           QName JavaDoc qName,
780                           TermBinding term,
781                           WildcardBinding wildcard,
782                           boolean trace)
783    {
784       AddMethodMetaData addMetaData = wildcard == null ? null : wildcard.getAddMethodMetaData();
785       if(addMetaData == null)
786       {
787          addMetaData = term.getAddMethodMetaData();
788       }
789
790       if(addMetaData == null)
791       {
792          return false;
793       }
794
795       if(trace)
796       {
797          log.trace("setParent " + qName + " add");
798       }
799       invokeAdd(qName, addMetaData, owner, o);
800       return true;
801    }
802
803    private boolean tryPut(Object JavaDoc owner, Object JavaDoc o, QName JavaDoc qName, TermBinding term, boolean trace)
804    {
805       if(term.getPutMethodMetaData() != null ||
806          term.getMapEntryMetaData() != null && owner instanceof Map JavaDoc)
807       {
808          if(trace)
809          {
810             log.trace("setParent " + qName + " mapPut");
811          }
812          invokePut(qName, term, owner, o);
813          return true;
814       }
815       return false;
816    }
817
818    private Class JavaDoc classForElement(ElementBinding element, Class JavaDoc parentClass)
819    {
820       Class JavaDoc cls;
821       TypeBinding type = element.getType();
822       QName JavaDoc typeQName = type.getQName();
823       if(typeQName != null && Constants.NS_XML_SCHEMA.equals(typeQName.getNamespaceURI()))
824       {
825          cls = SimpleTypeBindings.classForType(type.getQName().getLocalPart(), element.isNillable());
826       }
827       else
828       {
829          ElementBinding arrayItem = null;
830          if(!type.isSimple() && type.getAttributes().isEmpty())
831          {
832             ParticleBinding typeParticle = type.getParticle();
833             ModelGroupBinding modelGroup = (ModelGroupBinding)(typeParticle == null ? null : typeParticle.getTerm());
834             arrayItem = modelGroup == null ? null : modelGroup.getArrayItem();
835          }
836
837          if(arrayItem != null)
838          {
839             cls = classForElement(arrayItem, parentClass);
840             // todo: what's the best way to get an array class having the item class
841
cls = Array.newInstance(cls, 0).getClass();
842          }
843          else
844          {
845             cls = classForNonArrayItem(element, parentClass);
846          }
847       }
848       return cls;
849    }
850
851    private static void setMapEntryValue(MapEntryMetaData mapEntryMetaData, Object JavaDoc parent, Object JavaDoc o)
852    {
853       String JavaDoc getValueMethodName = mapEntryMetaData.getGetValueMethod();
854       if(getValueMethodName == null)
855       {
856          getValueMethodName = "getValue";
857       }
858
859       String JavaDoc setValueMethodName = mapEntryMetaData.getSetValueMethod();
860       if(setValueMethodName == null)
861       {
862          setValueMethodName = "setValue";
863       }
864
865       Class JavaDoc parentCls = parent.getClass();
866       Method JavaDoc setValueMethod = getSetMethod(parentCls, getValueMethodName, setValueMethodName);
867       invokeSetter(setValueMethod, parent, o, setValueMethodName);
868    }
869
870    private static void invokeSetter(Method JavaDoc setValueMethod, Object JavaDoc parent, Object JavaDoc o, String JavaDoc setValueMethodName)
871    {
872       try
873       {
874          setValueMethod.invoke(parent, new Object JavaDoc[]{o});
875       }
876       catch(Exception JavaDoc e)
877       {
878          throw new JBossXBRuntimeException("Failed to invoke " +
879             setValueMethodName +
880             " on " +
881             parent +
882             " with parameter " +
883             o +
884             ": " +
885             e.getMessage()
886          );
887       }
888    }
889
890    private static Method JavaDoc getSetMethod(Class JavaDoc cls, String JavaDoc getMethodName, String JavaDoc setMethodName)
891    {
892       Method JavaDoc getKeyMethod;
893       try
894       {
895          getKeyMethod = cls.getMethod(getMethodName, null);
896       }
897       catch(NoSuchMethodException JavaDoc e)
898       {
899          throw new JBossXBRuntimeException("Method " + getMethodName + " not found in " + cls);
900       }
901
902       Method JavaDoc setKeyMethod;
903       try
904       {
905          setKeyMethod = cls.getMethod(setMethodName, new Class JavaDoc[]{getKeyMethod.getReturnType()});
906       }
907       catch(NoSuchMethodException JavaDoc e)
908       {
909          throw new JBossXBRuntimeException("Method " +
910             setMethodName +
911             "(" +
912             getKeyMethod.getReturnType().getName() +
913             " p) not found in " +
914             cls
915          );
916       }
917       return setKeyMethod;
918    }
919
920    private static MapEntryMetaData getMapEntryMetaData(TermBinding term, QName JavaDoc qName)
921    {
922       MapEntryMetaData mapEntryMetaData = term.getMapEntryMetaData();
923       if(mapEntryMetaData == null)
924       {
925          String JavaDoc msg;
926          if(term.isModelGroup())
927          {
928             msg = "Term " +
929                qName +
930                " bound as map entry key or value but map entry metadata is not available for its parent term.";
931          }
932          else
933          {
934             ElementBinding element = (ElementBinding)term;
935             msg = "Element " +
936                qName +
937                " bound as map entry key or value but map entry metadata is not available for its parent element nor its " +
938                (element.getType().getQName() == null ?
939                "annonymous" :
940                element.getType().getQName().toString()
941                ) +
942                " type.";
943          }
944          throw new JBossXBRuntimeException(msg);
945       }
946       return mapEntryMetaData;
947    }
948
949    private static Object JavaDoc newInstance(Class JavaDoc cls, QName JavaDoc elementName, boolean useNoArgCtorIfFound)
950    {
951       Object JavaDoc o;
952       if(cls.isArray())
953       {
954          o = GenericValueContainer.FACTORY.array(cls.getComponentType());
955       }
956       else
957       {
958          Constructor JavaDoc[] ctors = cls.getConstructors();
959          if(ctors.length == 0)
960          {
961             throw new JBossXBRuntimeException(
962                "Class " + cls.getName() + " has no public constructors or the class reflects a primitive type or void"
963             );
964          }
965
966          if(useNoArgCtorIfFound)
967          {
968             try
969             {
970                Constructor JavaDoc ctor = cls.getConstructor(null);
971                o = ctor.newInstance(null);
972             }
973             catch(NoSuchMethodException JavaDoc e)
974             {
975                o = new ValueListInitializer().newValueList(ValueListHandler.NON_DEFAULT_CTOR, cls);
976             }
977             catch(Exception JavaDoc e)
978             {
979                throw new JBossXBRuntimeException("Failed to create an instance of " +
980                   cls +
981                   " using default constructor for element " +
982                   elementName + ": " + e.getMessage(), e
983                );
984             }
985          }
986          else if(ctors.length > 1 || ctors[0].getParameterTypes().length > 0)
987          {
988             o = new ValueListInitializer().newValueList(ValueListHandler.NON_DEFAULT_CTOR, cls);
989          }
990          else
991          {
992             try
993             {
994                o = ctors[0].newInstance(null);
995             }
996             catch(Exception JavaDoc e)
997             {
998                throw new JBossXBRuntimeException("Failed to create an instance of " +
999                   cls +
1000                  " using default constructor for element " +
1001                  elementName + ": " + e.getMessage(), e
1002               );
1003            }
1004         }
1005      }
1006      return o;
1007   }
1008
1009   private static Class JavaDoc loadClassForTerm(String JavaDoc className,
1010                                         boolean ignoreCNFE,
1011                                         QName JavaDoc elementName)
1012   {
1013      if(className == null)
1014      {
1015         throw new JBossXBRuntimeException("No class for " + elementName);
1016      }
1017
1018      Class JavaDoc cls = null;
1019      try
1020      {
1021         cls = Thread.currentThread().getContextClassLoader().loadClass(className);
1022      }
1023      catch(ClassNotFoundException JavaDoc e)
1024      {
1025         if(ignoreCNFE)
1026         {
1027            if(log.isTraceEnabled())
1028            {
1029               log.trace("Failed to resolve class for element " +
1030                  elementName +
1031                  ": " +
1032                  className
1033               );
1034            }
1035         }
1036         else
1037         {
1038            throw new JBossXBRuntimeException("Failed to resolve class name for " +
1039               elementName +
1040               ": " +
1041               e.getMessage()
1042            );
1043         }
1044      }
1045      return cls;
1046   }
1047
1048   private void invokeAdd(QName JavaDoc qName, AddMethodMetaData addMethodMetaData, Object JavaDoc owner, Object JavaDoc o)
1049   {
1050      Class JavaDoc valueType = Object JavaDoc.class;
1051      if(addMethodMetaData.getValueType() != null)
1052      {
1053         try
1054         {
1055            valueType = Thread.currentThread().getContextClassLoader().
1056               loadClass(addMethodMetaData.getValueType());
1057         }
1058         catch(ClassNotFoundException JavaDoc e)
1059         {
1060            throw new JBossXBRuntimeException("Failed to load value type for addMethod.name=" +
1061               addMethodMetaData.getMethodName() +
1062               ", valueType=" +
1063               addMethodMetaData.getValueType() +
1064               ": " + e.getMessage(), e
1065            );
1066         }
1067      }
1068      else if(addMethodMetaData.isChildType())
1069      {
1070         if(o == null)
1071         {
1072            throw new JBossXBRuntimeException("addMethod=" +
1073               addMethodMetaData.getMethodName() +
1074               " for element " +
1075               qName +
1076               " is configured with valueType='child'. The valueType cannot be determined because" +
1077               " the child is null"
1078            );
1079         }
1080         valueType = o.getClass();
1081      }
1082
1083      Class JavaDoc ownerClass = owner.getClass();
1084      Method JavaDoc addMethod;
1085      try
1086      {
1087         addMethod = ownerClass.getMethod(addMethodMetaData.getMethodName(), new Class JavaDoc[]{valueType});
1088      }
1089      catch(NoSuchMethodException JavaDoc e)
1090      {
1091         throw new JBossXBRuntimeException("Failed to find addMethod.name=" +
1092            addMethodMetaData.getMethodName() +
1093            ", addMethod.valueType=" +
1094            valueType.getName() +
1095            " in class " +
1096            ownerClass.getName() +
1097            ": " +
1098            e.getMessage(), e
1099         );
1100      }
1101
1102      try
1103      {
1104         addMethod.invoke(owner, new Object JavaDoc[]{o});
1105      }
1106      catch(Exception JavaDoc e)
1107      {
1108         throw new JBossXBRuntimeException("setParent failed for " +
1109            qName +
1110            "=" +
1111            o +
1112            ": addMethod=" +
1113            addMethodMetaData.getMethodName() +
1114            " threw an exception for owner=" +
1115            owner +
1116            ", value=" +
1117            o +
1118            ": " +
1119            e.getMessage(),
1120            e
1121         );
1122      }
1123   }
1124
1125   private void invokePut(QName JavaDoc qName, TermBinding term, Object JavaDoc owner, Object JavaDoc o)
1126   {
1127      PutMethodMetaData putMethodMetaData = term.getPutMethodMetaData();
1128
1129      MapEntryMetaData mapEntryMetaData = term.getMapEntryMetaData();
1130      if(mapEntryMetaData == null)
1131      {
1132         throw new JBossXBRuntimeException((putMethodMetaData == null ?
1133            "Parent object is an instance of java.util.Map" :
1134            "putMethod is specified for element " + qName
1135            ) +
1136            " but mapEntry is specified for neither element " +
1137            qName +
1138            " nor it's type."
1139         );
1140      }
1141
1142      Class JavaDoc oClass = o.getClass();
1143      String JavaDoc getKeyMethodName = mapEntryMetaData.getGetKeyMethod();
1144      if(getKeyMethodName == null)
1145      {
1146         getKeyMethodName = "getKey";
1147      }
1148
1149      Method JavaDoc keyMethod;
1150      try
1151      {
1152         keyMethod = oClass.getMethod(getKeyMethodName, null);
1153      }
1154      catch(NoSuchMethodException JavaDoc e)
1155      {
1156         throw new JBossXBRuntimeException("setParent failed for " +
1157            qName +
1158            "=" +
1159            o +
1160            ": getKeyMethod=" +
1161            getKeyMethodName +
1162            " not found in " + oClass
1163         );
1164      }
1165
1166      Object JavaDoc key;
1167      try
1168      {
1169         key = keyMethod.invoke(o, null);
1170      }
1171      catch(Exception JavaDoc e)
1172      {
1173         throw new JBossXBRuntimeException("setParent failed for " +
1174            qName +
1175            "=" +
1176            o +
1177            ": getKeyMethod=" +
1178            getKeyMethodName +
1179            " threw an exception: " + e.getMessage(), e
1180         );
1181      }
1182
1183      Class JavaDoc keyType = Object JavaDoc.class;
1184      Class JavaDoc valueType = Object JavaDoc.class;
1185      String JavaDoc putMethodName = "put";
1186      Class JavaDoc ownerClass = owner.getClass();
1187
1188      if(putMethodMetaData != null)
1189      {
1190         if(putMethodMetaData.getKeyType() != null)
1191         {
1192            try
1193            {
1194               keyType = Thread.currentThread().getContextClassLoader().loadClass(putMethodMetaData.getKeyType());
1195            }
1196            catch(ClassNotFoundException JavaDoc e)
1197            {
1198               throw new JBossXBRuntimeException("setParent failed for " + qName + ": " + e.getMessage(), e);
1199            }
1200         }
1201
1202         if(putMethodMetaData.getValueType() != null)
1203         {
1204            try
1205            {
1206               valueType = Thread.currentThread().getContextClassLoader().loadClass(putMethodMetaData.getValueType());
1207            }
1208            catch(ClassNotFoundException JavaDoc e)
1209            {
1210               throw new JBossXBRuntimeException("setParent failed for " + qName + ": " + e.getMessage(), e);
1211            }
1212         }
1213
1214         String JavaDoc name = putMethodMetaData.getName();
1215         if(name != null)
1216         {
1217            putMethodName = name;
1218         }
1219      }
1220
1221      Method JavaDoc putMethod;
1222      try
1223      {
1224         putMethod = ownerClass.getMethod(putMethodName, new Class JavaDoc[]{keyType, valueType});
1225      }
1226      catch(NoSuchMethodException JavaDoc e)
1227      {
1228         throw new JBossXBRuntimeException("setParent failed for " +
1229            qName +
1230            "=" +
1231            o +
1232            ": putMethod=" +
1233            putMethodName +
1234            "(" + keyType.getName() + " key, " + valueType.getName() + " value) not found in " + ownerClass
1235         );
1236      }
1237
1238      Object JavaDoc value = o;
1239      String JavaDoc valueMethodName = mapEntryMetaData.getGetValueMethod();
1240      if(valueMethodName != null)
1241      {
1242         Method JavaDoc valueMethod;
1243         try
1244         {
1245            valueMethod = oClass.getMethod(valueMethodName, null);
1246         }
1247         catch(NoSuchMethodException JavaDoc e)
1248         {
1249            throw new JBossXBRuntimeException("setParent failed for " +
1250               qName +
1251               "=" +
1252               o +
1253               ": getValueMethod=" +
1254               mapEntryMetaData.getGetValueMethod() +
1255               " not found in " + oClass
1256            );
1257         }
1258
1259         try
1260         {
1261            value = valueMethod.invoke(o, null);
1262         }
1263         catch(Exception JavaDoc e)
1264         {
1265            throw new JBossXBRuntimeException("setParent failed for " +
1266               qName +
1267               "=" +
1268               o +
1269               ": getValueMethod=" +
1270               mapEntryMetaData.getGetValueMethod() +
1271               " threw an exception: " + e.getMessage(), e
1272            );
1273         }
1274      }
1275      else if(o instanceof MapEntry)
1276      {
1277         value = ((MapEntry)o).getValue();
1278      }
1279
1280      try
1281      {
1282         putMethod.invoke(owner, new Object JavaDoc[]{key, value});
1283      }
1284      catch(Exception JavaDoc e)
1285      {
1286         throw new JBossXBRuntimeException("setParent failed for " +
1287            qName +
1288            "=" +
1289            o +
1290            ": putMethod=" +
1291            putMethodName +
1292            " threw an exception for owner=" +
1293            owner +
1294            ", key=" +
1295            key +
1296            ", value=" +
1297            value +
1298            ": " +
1299            e.getMessage(),
1300            e
1301         );
1302      }
1303   }
1304
1305   private Class JavaDoc classForNonArrayItem(ElementBinding element, Class JavaDoc parentClass)
1306   {
1307      String JavaDoc clsName;
1308
1309      // first, class metadata and map entry metadata
1310
ClassMetaData clsMetaData = element.getClassMetaData();
1311      clsName = clsMetaData == null ? null : clsMetaData.getImpl();
1312      if(clsName == null)
1313      {
1314         MapEntryMetaData mapEntryMetaData = element.getMapEntryMetaData();
1315         if(mapEntryMetaData != null)
1316         {
1317            clsName = mapEntryMetaData.getImpl();
1318            if(clsName == null)
1319            {
1320               clsName = MapEntry.class.getName();
1321            }
1322         }
1323      }
1324
1325      // second, property metadata and property type
1326
if(clsName == null)
1327      {
1328         if(parentClass == null)
1329         {
1330            clsName = classFromQName(element);
1331         }
1332         else
1333         {
1334            PropertyMetaData propertyMetaData = element.getPropertyMetaData();
1335            String JavaDoc propName = propertyMetaData == null ? null : propertyMetaData.getName();
1336            if(propName == null)
1337            {
1338               // if there is add or put method metadata then fallback to XML-name-to-class-name algorithm
1339
if(element.getAddMethodMetaData() == null && element.getPutMethodMetaData() == null)
1340               {
1341                  propName =
1342                     Util.xmlNameToFieldName(element.getQName().getLocalPart(), element.getSchema().isIgnoreLowLine());
1343               }
1344            }
1345
1346            if(propName != null)
1347            {
1348               FieldInfo fieldInfo = FieldInfo.getFieldInfo(parentClass, propName, false);
1349               Class JavaDoc fieldType = fieldInfo == null ? null : fieldInfo.getType();
1350
1351               if(fieldType == null ||
1352                  Modifier.isAbstract(fieldType.getModifiers()) ||
1353                  Modifier.isInterface(fieldType.getModifiers()) ||
1354                  fieldType.isArray() ||
1355                  Collection JavaDoc.class.isAssignableFrom(fieldType))
1356               {
1357                  clsName = classFromQName(element);
1358               }
1359               else
1360               {
1361                  return fieldType;
1362               }
1363            }
1364         }
1365      }
1366
1367      return loadClassForTerm(clsName, element.getSchema().isIgnoreUnresolvedFieldOrClass(), element.getQName());
1368   }
1369
1370   private String JavaDoc classFromQName(ElementBinding element)
1371   {
1372      String JavaDoc clsName;
1373      QName JavaDoc typeBase = element.getType().getQName();
1374      if(typeBase == null)
1375      {
1376         typeBase = element.getQName();
1377      }
1378
1379      SchemaBinding schema = element.getSchema();
1380      PackageMetaData pkgMetaData = schema.getPackageMetaData();
1381      if(pkgMetaData == null)
1382      {
1383         clsName =
1384            Util.xmlNameToClassName(typeBase.getNamespaceURI(),
1385               typeBase.getLocalPart(),
1386               schema.isIgnoreLowLine()
1387            );
1388      }
1389      else
1390      {
1391         String JavaDoc pkg = pkgMetaData.getName();
1392         clsName =
1393            pkg == null || pkg.length() == 0 ?
1394            Util.xmlNameToClassName(typeBase.getLocalPart(), schema.isIgnoreLowLine()) :
1395            pkg + "." + Util.xmlNameToClassName(typeBase.getLocalPart(), schema.isIgnoreLowLine());
1396      }
1397      return clsName;
1398   }
1399
1400   private static Class JavaDoc classForSimpleType(TypeBinding type, boolean nillable)
1401   {
1402      ValueMetaData valueMetaData = type.getValueMetaData();
1403      if(valueMetaData != null && valueMetaData.getUnmarshalMethod() != null)
1404      {
1405         return RtUtil.getUnmarshalMethod(type.getQName(), valueMetaData).getReturnType();
1406      }
1407      else if(type.getClassMetaData() != null && type.getClassMetaData().getImpl() != null)
1408      {
1409         return RtUtil.loadClass(type.getClassMetaData().getImpl(), true);
1410      }
1411
1412      TypeBinding itemType = type.getItemType();
1413      if(itemType != null)
1414      {
1415         if(type.getSchemaBinding().isUnmarshalListsToArrays())
1416         {
1417            // todo: nillable not always should be propagated to the item
1418
Class JavaDoc itemClass = classForSimpleType(itemType, nillable);
1419            return Array.newInstance(itemClass, 0).getClass();
1420         }
1421         else
1422         {
1423            return java.util.List JavaDoc.class;
1424         }
1425      }
1426      else
1427      {
1428         QName JavaDoc qName = type.getQName();
1429         if(qName != null && Constants.NS_XML_SCHEMA.equals(qName.getNamespaceURI()))
1430         {
1431            return SimpleTypeBindings.classForType(qName.getLocalPart(), nillable);
1432         }
1433         else
1434         {
1435            TypeBinding baseType = type.getBaseType();
1436            if(baseType == null)
1437            {
1438               throw new JBossXBRuntimeException("Expected a base type here.");
1439            }
1440
1441            return classForSimpleType(baseType, nillable);
1442         }
1443      }
1444   }
1445}
1446
Popular Tags