001: /*
002: Copyright (c) 2004-2005, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.model;
030:
031: import java.lang.reflect.Constructor;
032: import java.lang.reflect.InvocationTargetException;
033: import java.lang.reflect.Method;
034:
035: import org.jibx.binding.util.StringArray;
036: import org.jibx.runtime.IMarshallingContext;
037: import org.jibx.runtime.IUnmarshallingContext;
038: import org.jibx.runtime.JiBXException;
039: import org.jibx.runtime.QName;
040:
041: /**
042: * Model component for <i>string</i> attribute group in binding definition.
043: *
044: * @author Dennis M. Sosnoski
045: * @version 1.0
046: */
047:
048: public class StringAttributes extends AttributeBase {
049: /** Enumeration of allowed attribute names */
050: public static final StringArray s_allowedAttributes = new StringArray(
051: new String[] { "default", "deserializer", "serializer" });
052: // TODO: add "format" for 2.0
053:
054: //
055: // Constants and such related to code generation.
056:
057: // signature variants allowed for serializer
058: private static final String[] SERIALIZER_SIGNATURE_VARIANTS = {
059: "Lorg/jibx/runtime/IMarshallingContext;",
060: "Ljava/lang/Object;", "" };
061:
062: // signatures allowed for deserializer
063: private static final String[] DESERIALIZER_SIGNATURES = {
064: "(Ljava/lang/String;Lorg/jibx/runtime/IUnmarshallingContext;)",
065: "(Ljava/lang/String;Ljava/lang/Object;)",
066: "(Ljava/lang/String;)" };
067:
068: // signature required for constructor from string
069: private static final String STRING_CONSTRUCTOR_SIGNATURE = "(Ljava/lang/String;)";
070:
071: // classes of arguments to constructor or deserializer
072: private static final Class[] STRING_CONSTRUCTOR_ARGUMENT_CLASSES = { java.lang.String.class };
073:
074: //
075: // Instance data.
076:
077: /** Referenced format name. */
078: private String m_formatName;
079:
080: /** Format qualified name. */
081: private QName m_formatQName;
082:
083: /** Default value text. */
084: private String m_defaultText;
085:
086: /** Serializer fully qualified class and method name. */
087: private String m_serializerName;
088:
089: /** Deserializer fully qualified class and method name. */
090: private String m_deserializerName;
091:
092: /** Base format for conversions. */
093: private FormatElement m_baseFormat;
094:
095: /** Value type class. */
096: private IClass m_typeClass;
097:
098: /** Default value object. */
099: private Object m_default;
100:
101: /** Serializer method (or toString equivalent) information. */
102: private IClassItem m_serializerItem;
103:
104: /** Deserializer method (or constructor from string) information. */
105: private IClassItem m_deserializerItem;
106:
107: /**
108: * Default constructor.
109: */
110: public StringAttributes() {
111: }
112:
113: /**
114: * Set value type. This needs to be set by the owning element prior to
115: * validation. Even though the type is an important part of the string
116: * information, it's treated as a separate item of information because it
117: * needs to be used as part of the property attributes.
118: *
119: * @param type value type
120: */
121: public void setType(IClass type) {
122: m_typeClass = type;
123: }
124:
125: /**
126: * Get value type.
127: *
128: * @return value type
129: */
130: public IClass getType() {
131: return m_typeClass;
132: }
133:
134: /**
135: * Get base format name.
136: *
137: * @return referenced base format
138: */
139: public String getFormatName() {
140: return m_formatName;
141: }
142:
143: /**
144: * Set base format name.
145: *
146: * @param name referenced base format
147: */
148: public void setFormatName(String name) {
149: m_formatName = name;
150: m_formatQName = (name == null) ? null : new QName(name);
151: }
152:
153: /**
154: * Get format qualified name.
155: *
156: * @return format qualified name (<code>null</code> if none)
157: */
158: public QName getFormatQName() {
159: return m_formatQName;
160: }
161:
162: /**
163: * Set format qualified name. This method changes the label value to match
164: * the qualified name.
165: *
166: * @return format qualified name (<code>null</code> if none)
167: */
168: public void setFormatQName(QName qname) {
169: m_formatQName = qname;
170: m_formatName = (qname == null) ? null : qname.toString();
171: }
172:
173: /**
174: * Get default value text.
175: *
176: * @return default value text
177: */
178: public String getDefaultText() {
179: return m_defaultText;
180: }
181:
182: /**
183: * Get default value. This method is only usable after a
184: * call to {@link #validate}.
185: *
186: * @return default value object
187: */
188: public Object getDefault() {
189: return m_default;
190: }
191:
192: /**
193: * Set default value text.
194: *
195: * @param value default value text
196: */
197: public void setDefaultText(String value) {
198: m_defaultText = value;
199: }
200:
201: /**
202: * Get serializer name.
203: *
204: * @return fully qualified class and method name for serializer (or
205: * <code>null</code> if none)
206: */
207: public String getSerializerName() {
208: return m_serializerName;
209: }
210:
211: /**
212: * Get serializer method information. This method is only usable after a
213: * call to {@link #validate}.
214: *
215: * @return serializer information (or <code>null</code> if none)
216: */
217: public IClassItem getSerializer() {
218: return m_serializerItem;
219: }
220:
221: /**
222: * Set serializer method name.
223: *
224: * @param fully qualified class and method name for serializer
225: */
226: public void setSerializerName(String name) {
227: m_serializerName = name;
228: }
229:
230: /**
231: * Get deserializer name.
232: *
233: * @return fully qualified class and method name for deserializer (or
234: * <code>null</code> if none)
235: */
236: public String getDeserializerName() {
237: return m_deserializerName;
238: }
239:
240: /**
241: * Get deserializer method information. This method is only usable after a
242: * call to {@link #validate}.
243: *
244: * @return deserializer information (or <code>null</code> if none)
245: */
246: public IClassItem getDeserializer() {
247: return m_deserializerItem;
248: }
249:
250: /**
251: * Set deserializer method name.
252: *
253: * @param fully qualified class and method name for deserializer
254: */
255: public void setDeserializerName(String name) {
256: m_deserializerName = name;
257: }
258:
259: /**
260: * Get base format information. This method is only usable after a
261: * call to {@link #validate}.
262: *
263: * @return base format element (or <code>null</code> if none)
264: */
265: public FormatElement getBaseFormat() {
266: return m_baseFormat;
267: }
268:
269: /**
270: * JiBX access method to set format label as qualified name.
271: *
272: * @param label format label text (<code>null</code> if none)
273: * @param ictx unmarshalling context
274: * @throws JiBXException on deserialization error
275: */
276: private void setQualifiedFormat(String label,
277: IUnmarshallingContext ictx) throws JiBXException {
278: setFormatQName(QName.deserialize(label, ictx));
279: }
280:
281: /**
282: * JiBX access method to get format label as qualified name.
283: *
284: * @param ictx marshalling context
285: * @return format label text (<code>null</code> if none)
286: * @throws JiBXException on deserialization error
287: */
288: private String getQualifiedFormat(IMarshallingContext ictx)
289: throws JiBXException {
290: return QName.serialize(getFormatQName(), ictx);
291: }
292:
293: /* (non-Javadoc)
294: * @see org.jibx.binding.model.AttributeBase#prevalidate(org.jibx.binding.model.ValidationContext)
295: */
296: public void prevalidate(ValidationContext vctx) {
297:
298: // make sure the type has been configured
299: if (m_typeClass == null) {
300: vctx
301: .addFatal("Missing type information for conversion to string");
302: } else {
303:
304: // get the base format (if any)
305: DefinitionContext dctx = vctx.getDefinitions();
306: if (m_formatName == null) {
307: m_baseFormat = dctx.getBestFormat(m_typeClass);
308: } else {
309: m_baseFormat = dctx.getNamedFormat(m_formatName);
310: if (m_baseFormat == null) {
311: String name = m_formatName;
312: if (name.startsWith("{}")) {
313: name = name.substring(2);
314: }
315: vctx.addError("Unknown format " + name);
316: }
317: }
318:
319: // check specified serializer and deserializer
320: String tname = m_typeClass.getName();
321: if (vctx.isOutBinding()) {
322: if (m_serializerName != null) {
323:
324: // build all possible signature variations
325: String[] tsigs = ClassUtils.getSignatureVariants(
326: tname, vctx);
327: int vcnt = SERIALIZER_SIGNATURE_VARIANTS.length;
328: String[] msigs = new String[tsigs.length * vcnt];
329: for (int i = 0; i < tsigs.length; i++) {
330: for (int j = 0; j < vcnt; j++) {
331: msigs[i * vcnt + j] = "(" + tsigs[i]
332: + SERIALIZER_SIGNATURE_VARIANTS[j]
333: + ")Ljava/lang/String;";
334: }
335: }
336:
337: // find a matching static method
338: m_serializerItem = ClassUtils.findStaticMethod(
339: m_serializerName, msigs, vctx);
340: if (m_serializerItem == null) {
341: vctx.addError("Static serializer method "
342: + m_serializerName + " not found");
343: }
344:
345: } else {
346:
347: // try to find an inherited serializer
348: FormatElement ances = m_baseFormat;
349: while (ances != null) {
350: m_serializerItem = ances.getSerializer();
351: if (m_serializerItem == null) {
352: ances = ances.getBaseFormat();
353: } else {
354: break;
355: }
356: }
357: if (m_serializerItem == null) {
358: m_serializerItem = m_typeClass.getMethod(
359: "toString", "()Ljava/lang/String;");
360: if (m_serializerItem == null) {
361: vctx.addError("toString method not found");
362: }
363: }
364: }
365: }
366: if (vctx.isInBinding() || m_defaultText != null) {
367: if (m_deserializerName != null) {
368:
369: // find a matching static method
370: m_deserializerItem = ClassUtils.findStaticMethod(
371: m_deserializerName,
372: DESERIALIZER_SIGNATURES, vctx);
373: if (m_deserializerItem == null) {
374: vctx.addError("Static deserializer method "
375: + m_deserializerName + " not found");
376: } else {
377: String result = m_deserializerItem
378: .getTypeName();
379: if (!ClassUtils.isAssignable(result, tname,
380: vctx)) {
381: vctx.addError("Static deserializer method "
382: + m_deserializerName
383: + " has incompatible result type");
384: }
385: }
386:
387: } else {
388:
389: // check for a Java 5 enumeration
390: boolean isenum = false;
391: IClass sclas = m_typeClass;
392: while ((sclas = sclas.getSuperClass()) != null) {
393: if (sclas.getName().equals("java.lang.Enum")) {
394: isenum = true;
395: break;
396: }
397: }
398: if (isenum) {
399: m_deserializerItem = m_typeClass.getMethod(
400: "valueOf", "(Ljava/lang/String;)");
401: }
402: if (m_deserializerItem == null) {
403:
404: // try to find an inherited deserializer
405: FormatElement ances = m_baseFormat;
406: while (ances != null) {
407: m_deserializerItem = ances
408: .getDeserializer();
409: if (m_deserializerItem == null) {
410: ances = ances.getBaseFormat();
411: } else {
412: break;
413: }
414: }
415: if (m_deserializerItem == null) {
416:
417: // try to find constructor from string as last resort
418: m_deserializerItem = m_typeClass
419: .getInitializerMethod(STRING_CONSTRUCTOR_SIGNATURE);
420: if (m_deserializerItem == null) {
421:
422: // error unless predefined formats
423: if (vctx.getNestingDepth() > 0) {
424: StringBuffer buff = new StringBuffer();
425: buff
426: .append("Need deserializer or constructor ");
427: buff.append("from string");
428: if (!vctx.isInBinding()) {
429: buff
430: .append(" for default value of type ");
431: buff.append(tname);
432: } else {
433: buff.append(" for type ");
434: buff.append(tname);
435: }
436: vctx.addError(buff.toString());
437: }
438:
439: }
440: }
441: }
442: }
443: }
444:
445: // check for default value to be converted
446: if (m_defaultText != null && m_deserializerItem != null) {
447:
448: // first load the class to handle conversion
449: IClass iclas = m_deserializerItem.getOwningClass();
450: Class clas = iclas.loadClass();
451: Exception ex = null;
452: boolean construct = false;
453: try {
454: if (clas == null) {
455: vctx
456: .addError("Unable to load class "
457: + iclas.getName()
458: + " for converting default value of type "
459: + tname);
460: } else if (m_deserializerItem.isInitializer()) {
461:
462: // invoke constructor to process default value
463: construct = true;
464: Constructor cons = clas
465: .getConstructor(STRING_CONSTRUCTOR_ARGUMENT_CLASSES);
466: try {
467: cons.setAccessible(true);
468: } catch (Exception e) { /* deliberately left empty */
469: }
470: Object[] args = new Object[1];
471: args[0] = m_defaultText;
472: m_default = cons.newInstance(args);
473:
474: } else {
475:
476: // invoke deserializer to convert default value
477: String mname = m_deserializerItem.getName();
478: Method deser = clas.getDeclaredMethod(mname,
479: STRING_CONSTRUCTOR_ARGUMENT_CLASSES);
480: try {
481: deser.setAccessible(true);
482: } catch (Exception e) { /* deliberately left empty */
483: }
484: Object[] args = new Object[1];
485: args[0] = m_defaultText;
486: m_default = deser.invoke(null, args);
487:
488: }
489: } catch (SecurityException e) {
490: StringBuffer buff = new StringBuffer(
491: "Unable to access ");
492: if (construct) {
493: buff.append("constructor from string");
494: } else {
495: buff.append("deserializer ");
496: buff.append(m_deserializerName);
497: }
498: buff
499: .append(" for converting default value of type ");
500: buff.append(tname);
501: vctx.addError(buff.toString());
502: } catch (NoSuchMethodException e) {
503: StringBuffer buff = new StringBuffer(
504: "Unable to find ");
505: if (construct) {
506: buff.append("constructor from string");
507: } else {
508: buff.append("deserializer ");
509: buff.append(m_deserializerName);
510: }
511: buff
512: .append(" for converting default value of type ");
513: buff.append(tname);
514: vctx.addError(buff.toString());
515: } catch (IllegalArgumentException e) {
516: ex = e;
517: } catch (InstantiationException e) {
518: ex = e;
519: } catch (IllegalAccessException e) {
520: ex = e;
521: } catch (InvocationTargetException e) {
522: ex = e;
523: } finally {
524: if (ex != null) {
525: StringBuffer buff = new StringBuffer(
526: "Error calling ");
527: if (construct) {
528: buff.append("constructor from string");
529: } else {
530: buff.append("deserializer ");
531: buff.append(m_deserializerName);
532: }
533: buff
534: .append(" for converting default value of type ");
535: buff.append(tname);
536: vctx.addError(buff.toString());
537: }
538: }
539: }
540: }
541: super.prevalidate(vctx);
542: }
543: }
|