001: /*
002: Copyright (c) 2003-2004, 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.extras;
030:
031: import java.lang.reflect.Array;
032: import java.util.ArrayList;
033:
034: import org.jibx.runtime.IAliasable;
035: import org.jibx.runtime.IMarshallable;
036: import org.jibx.runtime.IMarshaller;
037: import org.jibx.runtime.IMarshallingContext;
038: import org.jibx.runtime.IUnmarshaller;
039: import org.jibx.runtime.IUnmarshallingContext;
040: import org.jibx.runtime.JiBXException;
041: import org.jibx.runtime.impl.MarshallingContext;
042: import org.jibx.runtime.impl.UnmarshallingContext;
043:
044: /**
045: * <p>Custom marshaller/unmarshaller for reference arrays of a particular type.
046: * This handles mapping arrays typed as <code>object-type[]</code>, where the
047: * <i>object-type</i> is any class name (not a primitive type). All items in the
048: * array must be of a mapped type. If a name is specified by the mapping
049: * definition that name is used as a wrapper around the elements representing
050: * the items in the array; otherwise, the elements are just handled inline.</p>
051: *
052: * @author Dennis M. Sosnoski
053: * @version 1.0
054: */
055:
056: public class TypedArrayMapper implements IMarshaller, IUnmarshaller,
057: IAliasable {
058:
059: private static final Object[] DUMMY_ARRAY = {};
060:
061: private String m_uri;
062: private int m_index;
063: private String m_name;
064: private Object[] m_baseArray;
065: private ArrayList m_holder;
066:
067: /**
068: * Aliased constructor. This takes a name definition for the top-level
069: * wrapper element. It'll be used by JiBX when a name is supplied by the
070: * mapping which references this custom marshaller/unmarshaller.
071: *
072: * @param uri namespace URI for the top-level element
073: * @param index namespace index corresponding to the defined URI within the
074: * marshalling context definitions
075: * @param name local name for the top-level element
076: * @param type class name for type of items in array
077: */
078:
079: public TypedArrayMapper(String uri, int index, String name,
080: String type) {
081:
082: // save the simple values
083: m_uri = uri;
084: m_index = index;
085: m_name = name;
086:
087: // strip trailing array bracket sets from type (at least one)
088: int dimen = 0;
089: while (type.endsWith("[]")) {
090: type = type.substring(0, type.length() - 2);
091: dimen++;
092: }
093:
094: // now get the class used for array items
095: try {
096:
097: // first try loading item class from context classloader
098: Class clas = null;
099: ClassLoader loader = Thread.currentThread()
100: .getContextClassLoader();
101: if (loader != null) {
102: try {
103: clas = loader.loadClass(type);
104: } catch (ClassNotFoundException e) { /* fall through */
105: }
106: }
107: if (clas == null) {
108:
109: // if not found, try the loader that loaded this class
110: clas = UnmarshallingContext.class.getClassLoader()
111: .loadClass(type);
112: }
113:
114: // create a dummy base array of specified type
115: int[] dimens = new int[dimen];
116: m_baseArray = (Object[]) Array.newInstance(clas, dimens);
117:
118: } catch (ClassNotFoundException e) {
119: throw new IllegalArgumentException(
120: "Error loading array item class " + type + ": "
121: + e.getMessage());
122: }
123: }
124:
125: /**
126: * Class only constructor. This just sets up for an XML representation with
127: * no element wrapping the actual item structures. It'll be used by JiBX
128: * when no name information is supplied by the mapping which references this
129: * custom marshaller/unmarshaller.
130: *
131: * @param type class name for type of items in array
132: */
133:
134: public TypedArrayMapper(String type) {
135: this (null, 0, null, type);
136: }
137:
138: /* (non-Javadoc)
139: * @see org.jibx.runtime.IMarshaller#isExtension(int)
140: */
141:
142: public boolean isExtension(int index) {
143: return false;
144: }
145:
146: /* (non-Javadoc)
147: * @see org.jibx.runtime.IMarshaller#marshal(java.lang.Object,
148: * org.jibx.runtime.IMarshallingContext)
149: */
150:
151: public void marshal(Object obj, IMarshallingContext ictx)
152: throws JiBXException {
153:
154: // make sure the parameters are as expected
155: if (obj == null) {
156: if (m_name == null) {
157: throw new JiBXException(
158: "null array not allowed without wrapper");
159: }
160: } else if (!(ictx instanceof MarshallingContext)) {
161: throw new JiBXException(
162: "Marshalling context not of expected type");
163: } else {
164:
165: // verify object as a handled array type
166: Class clas = obj.getClass();
167: if (!clas.isArray()) {
168: throw new JiBXException(
169: "Invalid object type for marshaller");
170: } else {
171:
172: // start by generating start tag for container
173: MarshallingContext ctx = (MarshallingContext) ictx;
174: Object[] array = (Object[]) obj;
175: if (m_name != null) {
176: ctx.startTag(m_index, m_name);
177: }
178:
179: // loop through all entries in array
180: for (int i = 0; i < array.length; i++) {
181: Object item = array[i];
182: if (item == null) {
183: throw new JiBXException("Null value at offset "
184: + i + " not supported");
185: } else if (item instanceof IMarshallable) {
186: ((IMarshallable) item).marshal(ctx);
187: } else {
188: throw new JiBXException("Array item of type "
189: + item.getClass().getName()
190: + " does not implement "
191: + "org.jibx.runtime.IMarshallable");
192: }
193: }
194:
195: // finish with end tag for container element
196: if (m_name != null) {
197: ctx.endTag(m_index, m_name);
198: }
199:
200: }
201: }
202: }
203:
204: /* (non-Javadoc)
205: * @see org.jibx.runtime.IUnmarshaller#isPresent(org.jibx.runtime.IUnmarshallingContext)
206: */
207:
208: public boolean isPresent(IUnmarshallingContext ctx)
209: throws JiBXException {
210: return ctx.isAt(m_uri, m_name);
211: }
212:
213: /* (non-Javadoc)
214: * @see org.jibx.runtime.IUnmarshaller#unmarshal(java.lang.Object,
215: * org.jibx.runtime.IUnmarshallingContext)
216: */
217:
218: public Object unmarshal(Object obj, IUnmarshallingContext ictx)
219: throws JiBXException {
220:
221: // make sure we're at the appropriate start tag
222: UnmarshallingContext ctx = (UnmarshallingContext) ictx;
223: if (m_name != null) {
224: if (ctx.isAt(m_uri, m_name)) {
225: ctx.parsePastStartTag(m_uri, m_name);
226: } else {
227: return null;
228: }
229: }
230:
231: // create new array if needed
232: if (m_holder == null) {
233: m_holder = new ArrayList();
234: }
235:
236: // process all items present in document
237: while (!ctx.isEnd()) {
238: Object item = ctx.unmarshalElement();
239: m_holder.add(item);
240: }
241:
242: // discard close tag if used
243: if (m_name != null) {
244: ctx.parsePastEndTag(m_uri, m_name);
245: }
246:
247: // return array containing all items
248: Object[] result = m_holder.toArray(m_baseArray);
249: m_holder.clear();
250: return result;
251: }
252: }
|