001: /*
002: Copyright (c) 2004-2007, 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.util.ArrayList;
032:
033: import org.jibx.binding.util.StringArray;
034: import org.jibx.runtime.EnumSet;
035:
036: /**
037: * Model component for <i>property</i> attribute group in binding definition.
038: *
039: * @author Dennis M. Sosnoski
040: */
041: public class PropertyAttributes extends AttributeBase {
042: /** Enumeration of allowed attribute names */
043: public static final StringArray s_allowedAttributes = new StringArray(
044: new String[] { "field", "get-method", "set-method",
045: "test-method", "type", "usage" });
046:
047: // recognized test-method signatures.
048: private static final String[] TEST_METHOD_SIGNATURES = {
049: "(Lorg/jibx/runtime/IMarshallingContext;)Z", "()Z" };
050:
051: // recognized get-method signatures.
052: private static final String[] GET_METHOD_SIGNATURES = {
053: "(Lorg/jibx/runtime/IMarshallingContext;)", "()" };
054:
055: //
056: // Value set information
057:
058: public static final int REQUIRED_USAGE = 0;
059: public static final int OPTIONAL_USAGE = 1;
060: public static final int OPTIONAL_IN_USAGE = 2;
061: public static final int OPTIONAL_OUT_USAGE = 3;
062: private static final EnumSet s_usageEnum = new EnumSet(
063: REQUIRED_USAGE, new String[] { "required", "optional",
064: "opt-in", "opt-out" });
065:
066: //
067: // Instance data.
068:
069: /** Usage type code. */
070: private int m_usage;
071:
072: /** Usage name. */
073: private String m_usageName = s_usageEnum.getName(REQUIRED_USAGE);
074:
075: /** Property type name. */
076: private String m_declaredType;
077:
078: /** Property field name. */
079: private String m_fieldName;
080:
081: /** Test method name. */
082: private String m_testName;
083:
084: /** Get method name. */
085: private String m_getName;
086:
087: /** Set method name. */
088: private String m_setName;
089:
090: /** Type for value loaded on stack. */
091: private IClass m_getType;
092:
093: /** Type for value stored from stack. */
094: private IClass m_setType;
095:
096: /** Property type information. */
097: private IClass m_type;
098:
099: /** Property field information. */
100: private IClassItem m_fieldItem;
101:
102: /** Test method information. */
103: private IClassItem m_testItem;
104:
105: /** Get method information. */
106: private IClassItem m_getItem;
107:
108: /** Set method information. */
109: private IClassItem m_setItem;
110:
111: /** Flag for no actual property definition. */
112: private boolean m_isImplicit;
113:
114: /**
115: * Get usage name.
116: *
117: * @return usage name
118: */
119: public String getUsageName() {
120: return s_usageEnum.getName(m_usage);
121: }
122:
123: /**
124: * Get usage value. This method is only usable after a call to {@link
125: * #validate}.
126: *
127: * @return usage value
128: */
129: public int getUsage() {
130: return m_usage;
131: }
132:
133: /**
134: * Set usage name.
135: *
136: * @param name usage name
137: */
138: public void setUsageName(String name) {
139: m_usageName = name;
140: }
141:
142: /**
143: * Set usage value.
144: *
145: * @param use value
146: */
147: public void setUsage(int use) {
148: m_usage = use;
149: m_usageName = s_usageEnum.getName(use);
150: }
151:
152: /**
153: * Check if property is defined. This method is only usable after a call to
154: * {@link #validate}.
155: *
156: * @return <code>true</code> if property defined, <code>false</code> if not
157: */
158: public boolean hasProperty() {
159: return !m_isImplicit && m_type != null;
160: }
161:
162: /**
163: * Get declared type name.
164: *
165: * @return declared type name (or <code>null</code> if none)
166: */
167: public String getDeclaredType() {
168: return m_declaredType;
169: }
170:
171: /**
172: * Set declared type name.
173: *
174: * @param declared type name (or <code>null</code> if none)
175: */
176: public void setDeclaredType(String type) {
177: m_declaredType = type;
178: }
179:
180: /**
181: * Get field name.
182: *
183: * @return field name (or <code>null</code> if none)
184: */
185: public String getFieldName() {
186: return m_fieldName;
187: }
188:
189: /**
190: * Get field information. This method is only usable after a call to {@link
191: * #validate}.
192: *
193: * @return field information (or <code>null</code> if none)
194: */
195: public IClassItem getField() {
196: return m_fieldItem;
197: }
198:
199: /**
200: * Set field name.
201: *
202: * @param field field name (or <code>null</code> if none)
203: */
204: public void setFieldName(String field) {
205: m_fieldName = field;
206: }
207:
208: /**
209: * Get test method name.
210: *
211: * @return test method name (or <code>null</code> if none)
212: */
213: public String getTestName() {
214: return m_testName;
215: }
216:
217: /**
218: * Get test method information. This method is only usable after a call to
219: * {@link #validate}.
220: *
221: * @return test method information (or <code>null</code> if none)
222: */
223: public IClassItem getTest() {
224: return m_testItem;
225: }
226:
227: /**
228: * Set test method name.
229: *
230: * @param test test method name (or <code>null</code> if none)
231: */
232: public void setTestName(String test) {
233: m_testName = test;
234: }
235:
236: /**
237: * Get get method name.
238: *
239: * @return get method name (or <code>null</code> if none)
240: */
241: public String getGetName() {
242: return m_getName;
243: }
244:
245: /**
246: * Get get method information. This method is only usable after a call to
247: * {@link #validate}.
248: *
249: * @return get method information (or <code>null</code> if none)
250: */
251: public IClassItem getGet() {
252: return m_getItem;
253: }
254:
255: /**
256: * Get type for value loaded to stack. This method is only usable after a
257: * call to {@link #validate}.
258: *
259: * @return get value type (or <code>null</code> if none)
260: */
261: public IClass getGetType() {
262: return m_getType;
263: }
264:
265: /**
266: * Set get method name.
267: *
268: * @param get get method name (or <code>null</code> if none)
269: */
270: public void setGetName(String get) {
271: m_getName = get;
272: }
273:
274: /**
275: * Get set method name.
276: *
277: * @return set method name (or <code>null</code> if none)
278: */
279: public String getSetName() {
280: return m_setName;
281: }
282:
283: /**
284: * Get set method information. This method is only usable after a call to
285: * {@link #validate}.
286: *
287: * @return set method information (or <code>null</code> if none)
288: */
289: public IClassItem getSet() {
290: return m_setItem;
291: }
292:
293: /**
294: * Get type for value stored from stack. This method is only usable after a
295: * call to {@link #validate}.
296: *
297: * @return set value type (or <code>null</code> if none)
298: */
299: public IClass getSetType() {
300: return m_setType;
301: }
302:
303: /**
304: * Set set method name.
305: *
306: * @param set set method name (or <code>null</code> if none)
307: */
308: public void setSetName(String set) {
309: m_setName = set;
310: }
311:
312: /**
313: * Get type information. This method is only usable after a call to {@link
314: * #validate}.
315: *
316: * @return type information (or <code>null</code> if none)
317: */
318: public IClass getType() {
319: return m_type;
320: }
321:
322: /**
323: * Check if empty property definition. Empty property definitions occur
324: * because every <b>collection</b>, <b>structure</b>, and <b>value</b>
325: * element has associated property attributes but these may not actually
326: * reference a property (when using the containing object). This call is
327: * only meaningful after prevalidation.
328: *
329: * @return <code>true</code> if implicit property, <code>false</code> if not
330: */
331: public boolean isImplicit() {
332: return m_isImplicit;
333: }
334:
335: /* (non-Javadoc)
336: * @see org.jibx.binding.model.AttributeBase#prevalidate(org.jibx.binding.model.ValidationContext)
337: */
338: public void prevalidate(ValidationContext vctx) {
339:
340: // check usage value
341: if (m_usageName != null) {
342: m_usage = s_usageEnum.getValue(m_usageName);
343: if (m_usage < 0) {
344: vctx.addError("Value \"" + m_usageName
345: + "\" is not a valid choice for usage");
346: }
347: } else {
348: m_usage = vctx.getParentElement().getDefaultStyle();
349: }
350:
351: // handle basic lookups and checks
352: ContainerElementBase parent = vctx.getParentContainer();
353: IClass cobj = parent.getChildObjectType();
354: String dtype = null;
355: String gtype = null;
356: String stype = null;
357: boolean err = false;
358: m_isImplicit = true;
359: if (m_fieldName != null) {
360:
361: // field means this is real (not implicit)
362: m_isImplicit = false;
363:
364: // look up the field information
365: m_fieldItem = cobj.getField(m_fieldName);
366: if (m_fieldItem == null) {
367: vctx.addFatal("Nonstatic field " + m_fieldName
368: + " not found in class " + cobj.getName());
369: err = true;
370: } else {
371: dtype = gtype = stype = m_fieldItem.getTypeName();
372: }
373:
374: }
375: if (m_testName != null) {
376:
377: // make sure only used with optional
378: if (m_usage == REQUIRED_USAGE) {
379: vctx
380: .addError("test-method can only be used with optional property");
381: } else {
382:
383: // look up the method information
384: m_testItem = cobj.getMethod(m_testName,
385: TEST_METHOD_SIGNATURES);
386: if (m_testItem == null) {
387: vctx.addError("Nonstatic test-method " + m_testName
388: + " not found in class " + cobj.getName());
389: }
390: }
391:
392: }
393: if (m_getName != null) {
394:
395: // get-method means this is real (not implicit)
396: m_isImplicit = false;
397:
398: // look up the get method by name (no overload possible)
399: m_getItem = cobj
400: .getMethod(m_getName, GET_METHOD_SIGNATURES);
401: if (m_getItem == null) {
402: vctx.addFatal("Nonstatic get-method " + m_getName
403: + " not found in class " + cobj.getName());
404: err = true;
405: } else {
406: gtype = m_getItem.getTypeName();
407: if (dtype == null) {
408: dtype = gtype;
409: }
410: }
411:
412: // check for only get-method supplied when both directions needed
413: if (vctx.isInBinding() && m_fieldName == null
414: && m_setName == null) {
415: vctx
416: .addError("Need field or set-method for input handling");
417: }
418: }
419: if (m_setName != null) {
420:
421: // set-method means this is real (not implicit)
422: m_isImplicit = false;
423:
424: // need to handle overloads, so generate possible signatures
425: ArrayList sigs = new ArrayList();
426: if (m_getItem != null) {
427: String psig = ClassUtils.getSignature(gtype);
428: sigs.add("(" + psig
429: + "Lorg/jibx/runtime/IUnmarshallingContext;"
430: + ")V");
431: sigs.add("(" + psig + ")V");
432: }
433: if (m_declaredType != null) {
434: String psig = ClassUtils.getSignature(m_declaredType);
435: sigs.add("(" + psig
436: + "Lorg/jibx/runtime/IUnmarshallingContext;"
437: + ")V");
438: sigs.add("(" + psig + ")V");
439: }
440: if (m_fieldItem != null) {
441: String psig = m_fieldItem.getSignature();
442: sigs.add("(" + psig
443: + "Lorg/jibx/runtime/IUnmarshallingContext;"
444: + ")V");
445: sigs.add("(" + psig + ")V");
446: }
447: sigs
448: .add("(Ljava/lang/Object;Lorg/jibx/runtime/IUnmarshallingContext;)V");
449: sigs.add("(Ljava/lang/Object;)V");
450:
451: // match any of the possible signatures
452: m_setItem = cobj.getMethod(m_setName, (String[]) sigs
453: .toArray(new String[0]));
454: if (m_setItem == null && m_declaredType == null) {
455:
456: // nothing known about signature, try anything by name
457: m_setItem = cobj.getMethod(m_setName, "");
458: if (m_setItem != null
459: && (m_setItem.getArgumentCount() != 1 || !m_setItem
460: .getTypeName().equals("void"))) {
461: m_setItem = null;
462: }
463: if (m_setItem != null) {
464:
465: // make sure resulting type is compatible
466: String type = m_setItem.getArgumentType(0);
467: if (dtype != null
468: && !ClassUtils.isAssignable(type, dtype,
469: vctx)) {
470: m_setItem = null;
471: } else if (gtype != null
472: && !ClassUtils.isAssignable(type, gtype,
473: vctx)) {
474: m_setItem = null;
475: }
476: if (m_setItem != null) {
477: dtype = type;
478: }
479: }
480: }
481:
482: // check set-method found
483: if (m_setItem == null) {
484: vctx
485: .addFatal("Nonstatic set-method "
486: + m_setName
487: + " with argument of appropriate type not found in class "
488: + cobj.getName());
489: err = true;
490: } else {
491: stype = m_setItem.getArgumentType(0);
492: if (dtype == null) {
493: dtype = stype;
494: }
495: }
496:
497: // check for only set-method supplied when both directions needed
498: if (vctx.isOutBinding() && m_fieldName == null
499: && m_getName == null) {
500: vctx
501: .addError("Need field or get-method for output handling");
502: }
503: }
504:
505: // set the property type information
506: String tname = m_declaredType;
507: if (tname == null) {
508: tname = dtype;
509: if (tname == null) {
510: tname = cobj.getName();
511: }
512: } else if (dtype == null) {
513: dtype = gtype = stype = tname;
514: }
515: m_type = vctx.getClassInfo(tname);
516: if (m_type == null) {
517: vctx.addFatal("Unable to load class " + tname);
518: } else if (vctx.getContextObject() instanceof CollectionElement) {
519:
520: // forbid access specifications for child of collection
521: if (m_fieldName != null || m_getName != null
522: || m_setName != null) {
523: vctx
524: .addWarning("Property access attributes (field, "
525: + "get-method, set-method) ignored for collection item");
526: }
527:
528: } else if (!err && !m_isImplicit) {
529:
530: // check that type information is consistent
531: boolean valid = true;
532:
533: // require access specifications for child of non-collection
534: if (vctx.isInBinding()) {
535: if (stype == null) {
536: vctx.addError("No way to set property value");
537: stype = "java.lang.Object";
538: } else {
539: valid = ClassUtils.isAssignable(tname, stype, vctx)
540: || ClassUtils.isAssignable(stype, tname,
541: vctx);
542: }
543: m_setType = vctx.getClassInfo(stype);
544: }
545: if (gtype == null) {
546: if (vctx.isOutBinding()) {
547: vctx.addError("No way to get property value");
548: m_getType = vctx.getClassInfo("java.lang.Object");
549: }
550: } else {
551: if (valid) {
552: valid = ClassUtils.isAssignable(tname, gtype, vctx)
553: || ClassUtils.isAssignable(gtype, tname,
554: vctx);
555: }
556: m_getType = vctx.getClassInfo(gtype);
557: }
558: if (!valid) {
559: vctx
560: .addError("Incompatible types used in property definition");
561: }
562: }
563: super.prevalidate(vctx);
564: }
565: }
|