001: /*******************************************************************************
002: * Copyright (c) 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.editor.contentassist;
011:
012: import java.util.HashMap;
013: import java.util.TreeSet;
014:
015: import org.eclipse.pde.internal.core.ischema.ISchemaComplexType;
016: import org.eclipse.pde.internal.core.ischema.ISchemaCompositor;
017: import org.eclipse.pde.internal.core.ischema.ISchemaElement;
018: import org.eclipse.pde.internal.core.ischema.ISchemaObject;
019: import org.eclipse.pde.internal.core.text.IDocumentElementNode;
020:
021: /**
022: * XMLElementProposalComputer
023: *
024: */
025: public class XMLElementProposalComputer {
026:
027: /**
028: * @param sElement
029: * @param node
030: * @return A set of elements that can be added as children to element,
031: * <code>node</code>, as described by schema element, <code>sElement</code>,
032: * given multiplicity constraints and existing children found under
033: * <code>node</code>.
034: */
035: public static TreeSet computeElementProposal(
036: ISchemaElement sElement, IDocumentElementNode node) {
037: // Calculate the number of occurrences of each XML tag name
038: // in the node's direct children
039: HashMap tagNameMap = countXMLChildrenByTagName(node);
040: return computeElementProposal(sElement, tagNameMap);
041: }
042:
043: /**
044: * @param sElement
045: * @param tagNameMap
046: * @return
047: */
048: private static TreeSet computeElementProposal(
049: ISchemaElement sElement, HashMap tagNameMap) {
050:
051: TreeSet elementSet = new TreeSet(
052: new XMLElementProposalComparator());
053: // Get this element's compositor
054: ISchemaCompositor compositor = ((ISchemaComplexType) sElement
055: .getType()).getCompositor();
056: // Track multiplicity
057: int multiplicityTracker = 1;
058: // Process the compositor
059: computeCompositorChildProposal(compositor, elementSet,
060: tagNameMap, multiplicityTracker);
061: return elementSet;
062: }
063:
064: /**
065: * @param node
066: * @return A hash containing singleton entries of node's children mapped
067: * against the number of occurrences found
068: * Key is children's XML tag name
069: * Value is number of occurrences found amongst siblings
070: */
071: private static HashMap countXMLChildrenByTagName(
072: IDocumentElementNode node) {
073: IDocumentElementNode[] children = node.getChildNodes();
074: HashMap tagNameMap = new HashMap();
075: for (int i = 0; i < children.length; i++) {
076: String key = children[i].getXMLTagName();
077: if (tagNameMap.containsKey(key)) {
078: int value = ((Integer) tagNameMap.get(key)).intValue();
079: value++;
080: tagNameMap.put(key, new Integer(value));
081: } else {
082: tagNameMap.put(key, new Integer(1));
083: }
084: }
085: return tagNameMap;
086: }
087:
088: /**
089: * @param compositor
090: * @param proposalList
091: * @param siblings
092: * @param multiplicityTracker
093: */
094: private static void computeCompositorChildProposal(
095: ISchemaCompositor compositor, TreeSet elementSet,
096: HashMap siblings, int multiplicityTracker) {
097: // Compositor can be null only in cases where we had a schema complex
098: // type but that complex type was complex because it had attributes
099: // rather than element children
100: // All we care about is choices and sequences (Alls and groups not
101: // supported)
102: if (compositor == null) {
103: return;
104: } else if (compositor.getKind() == ISchemaCompositor.CHOICE) {
105: computeCompositorChoiceProposal(compositor, elementSet,
106: siblings, multiplicityTracker);
107: } else if (compositor.getKind() == ISchemaCompositor.SEQUENCE) {
108: computeCompositorSequenceProposal(compositor, elementSet,
109: siblings, multiplicityTracker);
110: }
111: }
112:
113: /**
114: * @param compositor
115: * @param elementSet
116: * @param siblings
117: * @param multiplicityTracker
118: */
119: private static void computeCompositorSequenceProposal(
120: ISchemaCompositor compositor, TreeSet elementSet,
121: HashMap siblings, int multiplicityTracker) {
122:
123: ISchemaObject[] schemaObject = compositor.getChildren();
124: // Unbounded max occurs are represented by the maximum integer value
125: if (multiplicityTracker < Integer.MAX_VALUE) {
126: // Multiply the max occurs amount to the overall multiplicity
127: multiplicityTracker = compositor.getMaxOccurs()
128: * multiplicityTracker;
129: }
130: // Process the compositors children
131: for (int i = 0; i < compositor.getChildCount(); i++) {
132: computeObjectChildProposal(schemaObject[i], elementSet,
133: siblings, multiplicityTracker);
134: }
135: }
136:
137: /**
138: * @param compositor
139: * @param elementSet
140: * @param siblings
141: * @param multiplicityTracker
142: */
143: private static void computeCompositorChoiceProposal(
144: ISchemaCompositor compositor, TreeSet elementSet,
145: HashMap siblings, int multiplicityTracker) {
146:
147: // Unbounded max occurs are represented by the maximum integer value
148: if (multiplicityTracker < Integer.MAX_VALUE) {
149: // Multiply the max occurs amount to the overall multiplicity
150: multiplicityTracker = compositor.getMaxOccurs()
151: * multiplicityTracker;
152: }
153: adjustChoiceSiblings(compositor, siblings);
154:
155: ISchemaObject[] schemaObject = compositor.getChildren();
156: // Process the compositors children
157: for (int i = 0; i < compositor.getChildCount(); i++) {
158: computeObjectChildProposal(schemaObject[i], elementSet,
159: siblings, multiplicityTracker);
160: }
161: }
162:
163: /**
164: * @param compositor
165: * @param siblings
166: */
167: private static void adjustChoiceSiblings(
168: ISchemaCompositor compositor, HashMap siblings) {
169:
170: ISchemaObject[] schemaObject = compositor.getChildren();
171: // Count the number of child element occurrences of the choice
172: // Compositor
173: int childElementCount = 0;
174: for (int i = 0; i < compositor.getChildCount(); i++) {
175: if (schemaObject[i] instanceof ISchemaElement) {
176: String name = schemaObject[i].getName();
177: if (siblings.containsKey(name)) {
178: int occurences = ((Integer) siblings.get(name))
179: .intValue();
180: childElementCount = childElementCount + occurences;
181: }
182: }
183: }
184: // Update all child element occurrences of the choice compositor
185: // to the number of occurences found
186: // Each choice occurence counts as one occurence for all child elements
187: // of that choice
188: // IMPORTANT: Any child of choice that is not an element (e.g.
189: // sequence, choice) is not supported, in future could recursively
190: // caculate, but time vs benefit is not worth it
191: for (int i = 0; i < compositor.getChildCount(); i++) {
192: if (schemaObject[i] instanceof ISchemaElement) {
193: String name = schemaObject[i].getName();
194: siblings.put(name, new Integer(childElementCount));
195: }
196: }
197: }
198:
199: /**
200: * @param schemaObject
201: * @param proposalList
202: * @param siblings
203: * @param multiplicityTracker
204: */
205: private static void computeObjectChildProposal(
206: ISchemaObject schemaObject, TreeSet elementSet,
207: HashMap siblings, int multiplicityTracker) {
208: if (schemaObject instanceof ISchemaElement) {
209: ISchemaElement schemaElement = (ISchemaElement) schemaObject;
210: computeElementChildProposal(schemaElement, elementSet,
211: siblings, multiplicityTracker);
212: } else if (schemaObject instanceof ISchemaCompositor) {
213: ISchemaCompositor sCompositor = (ISchemaCompositor) schemaObject;
214: computeCompositorChildProposal(sCompositor, elementSet,
215: siblings, multiplicityTracker);
216: }
217: }
218:
219: /**
220: * @param schemaElement
221: * @param proposalList
222: * @param siblings
223: * @param multiplicityTracker
224: */
225: private static void computeElementChildProposal(
226: ISchemaElement schemaElement, TreeSet elementSet,
227: HashMap siblings, int multiplicityTracker) {
228:
229: int occurrences = 0;
230: // Determine the number of occurrences found of this element
231: if (siblings.containsKey(schemaElement.getName())) {
232: occurrences = ((Integer) siblings.get(schemaElement
233: .getName())).intValue();
234: }
235: // Determine if the elements max occurrences is respected
236: if (multiplicityTracker < Integer.MAX_VALUE) {
237: multiplicityTracker = schemaElement.getMaxOccurs()
238: * multiplicityTracker;
239: }
240: // Only add a new proposal for a given element if it has not exceeded
241: // the multiplicity
242: // Note: This is a simple calculation that does not address all complex
243: // XML Schema multiplity rules. For instance, multiple layers of
244: // choices and sequences compositors coupled with varying siblings
245: // elements require a regex processor
246: // For the PDE space this is not required as extension point schemas
247: // are always very simple
248: if (occurrences < multiplicityTracker) {
249: elementSet.add(schemaElement);
250: }
251: }
252: }
|