001: /*
002: Copyright (c) 2003-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.def;
030:
031: import java.util.ArrayList;
032:
033: import org.jibx.binding.classes.*;
034: import org.jibx.runtime.JiBXException;
035:
036: /**
037: * Structure binding definition. This handles one or more child components,
038: * which may be ordered or unordered.
039: *
040: * @author Dennis M. Sosnoski
041: * @version 1.0
042: */
043: public class NestedStructure extends NestedBase {
044: //
045: // Method definitions used in code generation
046:
047: private static final String CHECK_ISSTART_NAME = "org.jibx.runtime.impl.UnmarshallingContext.isStart";
048: private static final String CHECK_ISSTART_SIGNATURE = "()Z";
049: private static final String SKIP_ELEMENT_NAME = "org.jibx.runtime.impl.UnmarshallingContext.skipElement";
050: private static final String SKIP_ELEMENT_SIGNATURE = "()V";
051: private static final String THROW_EXCEPTION_NAME = "org.jibx.runtime.impl.UnmarshallingContext.throwNameException";
052: private static final String THROW_EXCEPTION_SIGNATURE = "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V";
053:
054: //
055: // Instance data
056:
057: /** Child supplying ID for bound class. */
058: private IComponent m_idChild;
059:
060: /** Flag for choice of child content (used by subclasses). */
061: protected final boolean m_isChoice;
062:
063: /** Flag for duplicate values allowed when unmarshalling unordered group. */
064: private final boolean m_allowDuplicates;
065:
066: /** Flag for structure has associated object. */
067: private boolean m_hasObject;
068:
069: /** Flag for already linked (to avoid multiple passes). */
070: private boolean m_isLinked;
071:
072: /**
073: * Constructor.
074: *
075: * @param parent containing binding definition context
076: * @param objc current object context
077: * @param ord ordered content flag
078: * @param choice choice content flag
079: * @param flex flexible element handling flag
080: * @param ctx define context for structure flag
081: * @param hasobj has associated object flag
082: * @param dupl allow duplicates in unordered group flag
083: */
084: public NestedStructure(IContainer parent, IContextObj objc,
085: boolean ord, boolean choice, boolean flex, boolean ctx,
086: boolean hasobj, boolean dupl) {
087: super (parent, objc, ord, flex, ctx);
088: m_isChoice = choice;
089: m_hasObject = hasobj;
090: m_allowDuplicates = dupl;
091: }
092:
093: /**
094: * Set the object context.
095: *
096: * @param objc object context
097: */
098: public void setObjectContext(IContextObj objc) {
099: m_hasObject = false;
100: }
101:
102: //
103: // IComponent interface method definitions
104:
105: public boolean hasAttribute() {
106: return m_attributes != null && m_attributes.size() > 0;
107: }
108:
109: public void genAttrPresentTest(ContextMethodBuilder mb)
110: throws JiBXException {
111: if (m_attributes != null && m_attributes.size() > 0) {
112:
113: // if single possiblity just test it directly
114: int count = m_attributes.size();
115: if (count == 1) {
116: ((IComponent) m_attributes.get(0))
117: .genAttrPresentTest(mb);
118: } else {
119:
120: // generate code for chained test with branches to found exit
121: BranchWrapper[] tofound = new BranchWrapper[count];
122: for (int i = 0; i < count; i++) {
123: IComponent comp = (IComponent) m_attributes.get(i);
124: comp.genAttrPresentTest(mb);
125: tofound[i] = mb.appendIFNE(this );
126: }
127:
128: // fall off end of loop to push "false" on stack and jump to end
129: mb.appendICONST_0();
130: BranchWrapper toend = mb
131: .appendUnconditionalBranch(this );
132:
133: // generate found target to push "true" on stack and continue
134: for (int i = 0; i < count; i++) {
135: mb.targetNext(tofound[i]);
136: }
137: mb.appendICONST_1();
138: mb.targetNext(toend);
139:
140: }
141: } else {
142: throw new IllegalStateException(
143: "Internal error - no attributes present");
144: }
145: }
146:
147: public void genAttributeUnmarshal(ContextMethodBuilder mb)
148: throws JiBXException {
149: if (m_attributes != null && m_attributes.size() > 0) {
150: for (int i = 0; i < m_attributes.size(); i++) {
151: IComponent attr = (IComponent) m_attributes.get(i);
152: attr.genAttributeUnmarshal(mb);
153: }
154: } else {
155: throw new IllegalStateException(
156: "Internal error - no attributes present");
157: }
158: }
159:
160: public void genAttributeMarshal(ContextMethodBuilder mb)
161: throws JiBXException {
162: if (m_attributes != null && m_attributes.size() > 0) {
163: for (int i = 0; i < m_attributes.size(); i++) {
164: IComponent attr = (IComponent) m_attributes.get(i);
165: attr.genAttributeMarshal(mb);
166: }
167: } else {
168: throw new IllegalStateException(
169: "Internal error - no attributes present");
170: }
171: }
172:
173: public boolean hasContent() {
174: return m_contents.size() > 0;
175: }
176:
177: public void genContentUnmarshal(ContextMethodBuilder mb)
178: throws JiBXException {
179: if (m_contents.size() > 0) {
180:
181: // check for ordered or unordered content
182: if (m_isOrdered) {
183:
184: // just generate unmarshal code for each component in order
185: for (int i = 0; i < m_contents.size(); i++) {
186: IComponent child = (IComponent) m_contents.get(i);
187: child.genContentUnmarshal(mb);
188: }
189:
190: } else {
191:
192: // start by finding the number of required elements
193: int count = m_contents.size();
194: int nreq = 0;
195: for (int i = 0; i < count; i++) {
196: if (!((IComponent) m_contents.get(i)).isOptional()) {
197: nreq++;
198: }
199: }
200:
201: // create array for tracking elements seen
202: boolean useflag = nreq > 0 || !m_allowDuplicates;
203: if (useflag) {
204: mb.appendLoadConstant(count);
205: mb.appendCreateArray("boolean");
206: mb.defineSlot(this , ClassItem
207: .typeFromName("boolean[]"));
208: }
209:
210: // generate unmarshal loop code that checks for each component,
211: // branching to the next component until one is found and
212: // exiting the loop only when no component is matched (or in
213: // the case of flexible unmarshalling, only exiting the loop
214: // when the enclosing end tag is seen). this uses the array(s)
215: // of booleans to track elements seen and detect duplicates.
216: BranchWrapper link = null;
217: // TODO: initialize default values
218: BranchTarget first = mb.appendTargetNOP();
219: BranchWrapper[] toends;
220: if (m_isChoice) {
221: toends = new BranchWrapper[count + 1];
222: } else {
223: toends = new BranchWrapper[1];
224: }
225: for (int i = 0; i < count; i++) {
226:
227: // start with basic test code
228: if (link != null) {
229: mb.targetNext(link);
230: }
231: IComponent child = (IComponent) m_contents.get(i);
232: child.genContentPresentTest(mb);
233: link = mb.appendIFEQ(this );
234:
235: // check for duplicate (if enforced)
236: if (!m_allowDuplicates) {
237: genFlagTest(true, i, "Duplicate element ",
238: child.getWrapperName(), mb);
239: }
240:
241: // set flag for element seen
242: if (useflag
243: || !(child.isOptional() && m_allowDuplicates)) {
244: mb.appendLoadLocal(mb.getSlot(this ));
245: mb.appendLoadConstant(i);
246: mb.appendLoadConstant(1);
247: mb.appendASTORE("boolean");
248: }
249:
250: // generate actual unmarshalling code
251: child.genContentUnmarshal(mb);
252: BranchWrapper next = mb
253: .appendUnconditionalBranch(this );
254: if (m_isChoice) {
255: toends[i + 1] = next;
256: } else {
257: next.setTarget(first, mb);
258: }
259: }
260:
261: // handle comparison fall through depending on flexible flag
262: if (m_isFlexible) {
263: if (link != null) {
264:
265: // exit loop if not positioned at element start
266: mb.targetNext(link);
267: mb.loadContext();
268: mb.appendCallVirtual(CHECK_ISSTART_NAME,
269: CHECK_ISSTART_SIGNATURE);
270: toends[0] = mb.appendIFEQ(this );
271:
272: // ignore unknown element and loop back to start
273: mb.loadContext();
274: mb.appendCallVirtual(SKIP_ELEMENT_NAME,
275: SKIP_ELEMENT_SIGNATURE);
276: mb.appendUnconditionalBranch(this ).setTarget(
277: first, mb);
278:
279: }
280: } else {
281:
282: // set final test failure branch to fall through loop
283: toends[0] = link;
284: }
285:
286: // patch all branches that exit loop
287: mb.targetNext(toends);
288:
289: // handle required element present tests
290: if (nreq > 0) {
291: for (int i = 0; i < count; i++) {
292: IComponent child = (IComponent) m_contents
293: .get(i);
294: if (!child.isOptional()) {
295: genFlagTest(false, i,
296: "Missing required element ", child
297: .getWrapperName(), mb);
298: }
299: }
300: }
301: mb.freeSlot(this );
302:
303: }
304: } else {
305: throw new IllegalStateException(
306: "Internal error - no content present");
307: }
308: }
309:
310: /**
311: * Helper method to generate test code for value in boolean array. If the
312: * test fails, the generated code throws an exception with the appropriate
313: * error message.
314: *
315: * @param cond flag setting resulting in exception
316: * @param pos position of element in list of child components
317: * @param msg basic error message when test fails
318: * @param name
319: * @param mb
320: */
321: private void genFlagTest(boolean cond, int pos, String msg,
322: NameDefinition name, ContextMethodBuilder mb) {
323:
324: // generate code to load array item value
325: mb.appendLoadLocal(mb.getSlot(this ));
326: mb.appendLoadConstant(pos);
327: mb.appendALOAD("boolean");
328:
329: // append branch for case where test is passed
330: BranchWrapper ifgood;
331: if (cond) {
332: ifgood = mb.appendIFEQ(this );
333: } else {
334: ifgood = mb.appendIFNE(this );
335: }
336:
337: // generate exception for test failed
338: mb.loadContext();
339: mb.appendLoadConstant(msg);
340: if (name == null) {
341: mb.appendACONST_NULL();
342: mb.appendLoadConstant("(unknown name, position " + pos
343: + " in binding structure)");
344: } else {
345: name.genPushUriPair(mb);
346: }
347: mb.appendCallVirtual(THROW_EXCEPTION_NAME,
348: THROW_EXCEPTION_SIGNATURE);
349:
350: // set target for success branch on test
351: mb.targetNext(ifgood);
352: }
353:
354: public void genContentMarshal(ContextMethodBuilder mb)
355: throws JiBXException {
356: if (m_contents.size() > 0) {
357: for (int i = 0; i < m_contents.size(); i++) {
358: IComponent content = (IComponent) m_contents.get(i);
359: content.genContentMarshal(mb);
360: }
361: } else {
362: throw new IllegalStateException(
363: "Internal error - no content present");
364: }
365: }
366:
367: public String getType() {
368: if (m_hasObject) {
369: return super .getType();
370: } else if (m_attributes != null && m_attributes.size() > 0) {
371: return ((IComponent) m_attributes.get(0)).getType();
372: } else if (m_contents.size() > 0) {
373: return ((IComponent) m_contents.get(0)).getType();
374: } else {
375: throw new IllegalStateException("Internal error - "
376: + "no type defined for structure");
377: }
378: }
379:
380: public boolean hasId() {
381: return m_idChild != null;
382: }
383:
384: public void genLoadId(ContextMethodBuilder mb) throws JiBXException {
385: if (m_idChild == null) {
386: throw new IllegalStateException("No ID child defined");
387: } else {
388: m_idChild.genLoadId(mb);
389: }
390: }
391:
392: public void setLinkages() throws JiBXException {
393: if (!m_isLinked) {
394:
395: // set flag first in case of recursive reference
396: m_isLinked = true;
397:
398: // process all child components to link and sort by type
399: int i = 0;
400: while (i < m_contents.size()) {
401: IComponent comp = (IComponent) m_contents.get(i);
402: comp.setLinkages();
403: if (comp.hasAttribute()) {
404: if (m_attributes == null) {
405: m_attributes = new ArrayList();
406: }
407: m_attributes.add(comp);
408: }
409: if (!comp.hasContent()) {
410: m_contents.remove(i);
411: } else {
412: i++;
413: }
414: }
415: }
416: }
417:
418: // DEBUG
419: public void print(int depth) {
420: BindingDefinition.indent(depth);
421: System.out.print("structure "
422: + (m_isChoice ? "choice" : (m_isOrdered ? "ordered"
423: : "unordered")));
424: if (m_allowDuplicates) {
425: System.out.print(", duplicates allowed");
426: }
427: if (isFlexible()) {
428: System.out.print(", flexible");
429: }
430: if (m_idChild != null) {
431: System.out.print(" (ID)");
432: }
433: System.out.println();
434: for (int i = 0; i < m_contents.size(); i++) {
435: IComponent comp = (IComponent) m_contents.get(i);
436: comp.print(depth + 1);
437: }
438: if (m_attributes != null) {
439: for (int i = 0; i < m_attributes.size(); i++) {
440: IComponent comp = (IComponent) m_attributes.get(i);
441: comp.print(depth + 1);
442: }
443: }
444: }
445: }
|