001: /*
002: * The contents of this file are subject to the terms of the Common Development
003: * and Distribution License (the License). You may not use this file except in
004: * compliance with the License.
005: *
006: * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
007: * or http://www.netbeans.org/cddl.txt.
008: *
009: * When distributing Covered Code, include this CDDL Header Notice in each file
010: * and include the License file at http://www.netbeans.org/cddl.txt.
011: * If applicable, add the following below the CDDL Header, with the fields
012: * enclosed by brackets [] replaced by your own identifying information:
013: * "Portions Copyrighted [year] [name of copyright owner]"
014: *
015: * The Original Software is NetBeans. The Initial Developer of the Original
016: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
017: * Microsystems, Inc. All Rights Reserved.
018: */
019:
020: package org.netbeans.modules.soa.ui.axinodes;
021:
022: import java.awt.Color;
023: import java.awt.ComponentOrientation;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.List;
027: import java.util.ListIterator;
028: import java.util.Map;
029: import javax.xml.namespace.QName;
030: import org.netbeans.modules.soa.ui.SoaUiUtil;
031: import org.netbeans.modules.soa.ui.axinodes.NodeType.BadgeModificator;
032: import org.netbeans.modules.soa.ui.nodes.NodeFactory;
033: import org.netbeans.modules.xml.axi.AXIComponent;
034: import org.netbeans.modules.xml.axi.AXIContainer;
035: import org.netbeans.modules.xml.axi.AXIDocument;
036: import org.netbeans.modules.xml.axi.AXIType;
037: import org.netbeans.modules.xml.axi.AbstractAttribute;
038: import org.netbeans.modules.xml.axi.AbstractElement;
039: import org.netbeans.modules.xml.axi.Element;
040: import org.netbeans.modules.xml.axi.Attribute;
041: import org.netbeans.modules.xml.axi.datatype.NumberBase;
042: import org.netbeans.modules.xml.schema.model.Attribute.Use;
043: import org.netbeans.modules.xml.schema.model.Form;
044: import org.netbeans.modules.xml.schema.model.GlobalElement;
045: import org.netbeans.modules.xml.schema.model.GlobalType;
046: import org.netbeans.modules.xml.schema.model.LocalAttribute;
047: import org.netbeans.modules.xml.schema.model.LocalElement;
048: import org.netbeans.modules.xml.schema.model.SchemaComponent;
049: import org.netbeans.modules.xml.wsdl.model.Part;
050: import org.netbeans.modules.xml.xam.dom.AbstractDocumentComponent;
051: import org.netbeans.modules.xml.xam.dom.AbstractDocumentModel;
052: import org.netbeans.modules.xml.xam.dom.NamedComponentReference;
053: import org.openide.nodes.Node;
054: import org.openide.util.Lookup;
055:
056: /**
057: *
058: * @author nk160297
059: */
060: public class AxiomUtils {
061:
062: private static final String XPATH_ATTR_INDICATOR = "@"; // NOI18N
063: private static final String XPATH_SEPARATOR = "/"; // NOI18N
064: private static final String NAMESPACE_SEPARATOR = ":"; // NOI18N
065: private static final String DEFAULT_PREFIX = "ns"; // NOI18N
066:
067: /**
068: * Looks for an Axiom component which corresponds to the global schema component.
069: * Tye type paramether helps to narrow the search.
070: */
071: public static AXIComponent findGlobalComponent(
072: AXIDocument axiDocument,
073: Class<? extends AXIComponent> type,
074: SchemaComponent schemaComp) {
075: List<? extends AXIComponent> children;
076: if (type == null) {
077: children = axiDocument.getChildren();
078: } else {
079: children = axiDocument.getChildren(type);
080: }
081: //
082: for (AXIComponent globalChild : children) {
083: if (globalChild.getPeer() == schemaComp) {
084: return globalChild;
085: }
086: }
087: return null;
088: }
089:
090: /**
091: * Retruves subcomponents of the specifed axiom component and
092: * builds Nodes for each one.
093: */
094: public static Node[] processAxiComponent(
095: AXIComponent axiomComponent, Lookup lookup) {
096: if (axiomComponent == null) {
097: return new Node[0];
098: }
099: //
100: NodeFactory nodeFactory = (NodeFactory) lookup
101: .lookup(NodeFactory.class);
102: assert nodeFactory != null : "Node factory has to be specified"; // NOI18N
103: //
104: ArrayList<Node> nodesList = new ArrayList<Node>();
105: //
106: List<AbstractElement> elementsList = axiomComponent
107: .getChildElements();
108: for (AbstractElement element : elementsList) {
109: if (element instanceof Element) {
110: Node newNode = nodeFactory.createNode(NodeType.ELEMENT,
111: element, lookup);
112: if (newNode != null) {
113: nodesList.add(newNode);
114: }
115: }
116: }
117: //
118: if (axiomComponent instanceof AXIContainer) {
119: List<AbstractAttribute> attributesList = ((AXIContainer) axiomComponent)
120: .getAttributes();
121: for (AbstractAttribute attribute : attributesList) {
122: if (attribute instanceof Attribute) {
123: Node newNode = nodeFactory.createNode(
124: NodeType.ATTRIBUTE, attribute, lookup);
125: if (newNode != null) {
126: nodesList.add(newNode);
127: }
128: }
129: }
130: }
131: //
132: Node[] nodes = nodesList.toArray(new Node[nodesList.size()]);
133: return nodes;
134: }
135:
136: /**
137: * TODO This calculator works wrong because of the
138: * axiComponent.getParentElement() method returns null if the component is
139: * the reference to a global element. So an incomplete XPath can be constructed.
140: * @deprecated
141: */
142: public static String calculateSimpleXPath(AXIComponent axiComponent) {
143: //
144: // Collects Path Items first
145: ArrayList<PathItem> path = new ArrayList<PathItem>();
146: while (axiComponent != null) {
147: String compName = null;
148: //
149: if (axiComponent instanceof Element) {
150: compName = ((Element) axiComponent).getName();
151: } else if (axiComponent instanceof Attribute) {
152: compName = ((Attribute) axiComponent).getName();
153: } else if (axiComponent instanceof AXIType) {
154: compName = ((AXIType) axiComponent).getName();
155: }
156: //
157: String namespace = isUnqualified(axiComponent) ? ""
158: : axiComponent.getTargetNamespace();
159: //
160: if (compName != null && compName.length() != 0) {
161: PathItem pathItem = new PathItem(axiComponent,
162: namespace, compName, null);
163: path.add(pathItem);
164: } else {
165: path.clear();
166: }
167: //
168: axiComponent = axiComponent.getParentElement();
169: }
170: //
171: // Check namespaces.
172: // If namespace of a PathItem is the same as its parent has,
173: // then it doesn't necessary to show it.
174: // If a namespace is necessary, then try to obtain a prefix for it.
175: // ListIterator<PathItem> itr = path.listIterator();
176: // String prevItemNamespace = null;
177: // while (itr.hasNext()) {
178: // PathItem pathItem = itr.next();
179: // //
180: // if (prevItemNamespace != null) {
181: // if (prevItemNamespace.equals(pathItem.myNamespace)) {
182: // pathItem.myNamespace = null;
183: // continue;
184: // }
185: // }
186: // //
187: //// pathItem.myNamespace;
188: // //
189: // prevItem = pathItem;
190: // }
191: //
192: //
193: StringBuffer result = new StringBuffer();
194: ListIterator<PathItem> itr = path.listIterator(path.size());
195: while (itr.hasPrevious()) {
196: PathItem pathItem = itr.previous();
197: result.append(XPATH_SEPARATOR).append(pathItem.myLocalName);
198: }
199: //
200: return result.toString();
201: }
202:
203: /**
204: * Prepares XPath for the specified AXIOM node.
205: */
206: public static List<PathItem> prepareSimpleXPath(
207: final AxiomNode axiNode) {
208: //
209: // Collects Path Items first
210: ArrayList<PathItem> path = new ArrayList<PathItem>();
211: Node currNode = axiNode;
212: AxiomNode lastProcessedAxiomNode = null;
213: while (currNode != null && currNode instanceof AxiomNode) {
214: lastProcessedAxiomNode = (AxiomNode) currNode;
215: processNode(lastProcessedAxiomNode.getReference(), null,
216: path);
217: //
218: currNode = currNode.getParentNode();
219: }
220: //
221: // Add parent elements to ensure that the XPath would be absolute
222: if (lastProcessedAxiomNode != null) {
223: AXIComponent axiComponent = lastProcessedAxiomNode
224: .getReference();
225: if (axiComponent != null) {
226: AXIComponent parentAxiComponent = axiComponent
227: .getParent();
228: while (true) {
229: if (parentAxiComponent == null) {
230: break;
231: }
232: //
233: processNode(parentAxiComponent, null, path);
234: //
235: parentAxiComponent = parentAxiComponent.getParent();
236: }
237: }
238: }
239: //
240: return path;
241: }
242:
243: public static void processNode(final AXIComponent axiComponent,
244: String predicate, final ArrayList<PathItem> path) {
245: String compName = null;
246: //
247: if (axiComponent instanceof Element) {
248: compName = ((Element) axiComponent).getName();
249: } else if (axiComponent instanceof Attribute) {
250: compName = ((Attribute) axiComponent).getName();
251: } else if (axiComponent instanceof AXIType) {
252: compName = ((AXIType) axiComponent).getName();
253: }
254: //
255: if (compName != null && compName.length() != 0) {
256: String namespace = isUnqualified(axiComponent) ? null
257: : axiComponent.getTargetNamespace();
258: //
259: PathItem pathItem = new PathItem(axiComponent, namespace,
260: compName, predicate);
261: path.add(pathItem);
262: }
263: }
264:
265: public static String calculateSimpleXPath(final AxiomNode axiNode,
266: AbstractDocumentComponent adc) {
267: return calculateSimpleXPath(prepareSimpleXPath(axiNode), adc);
268: }
269:
270: /**
271: * This method can add new prefix definitions ot the specified WSDL.
272: */
273: public static String calculateSimpleXPath(
274: final List<PathItem> path, AbstractDocumentComponent adc) {
275: //
276: Map<String, String> prefixesMap = adc.getPrefixes();
277: Map<String, String> inversedPrefixesMap = getInverseMap(prefixesMap);
278: //
279: // Process namespaces.
280: // For each namespace looks for a prefix.
281: // Store the prefix in the PathItem if it's found.
282: // If prefix isn't found then a new prefix definition is registered in the WSDL.
283: ListIterator<PathItem> itr = path.listIterator();
284: String prevItemNamespace = null;
285: while (itr.hasNext()) {
286: PathItem pathItem = itr.next();
287: if (pathItem.myNamespace != null
288: && pathItem.myNamespace.length() > 0) {
289: //check added to handle unqualified elements
290: String prefix = getUniquePrefix(adc, prefixesMap,
291: inversedPrefixesMap, pathItem.myNamespace,
292: DEFAULT_PREFIX);
293: pathItem.myNamespacePrefix = prefix;
294: } else {
295: pathItem.myNamespacePrefix = "";
296: }
297: }
298: //
299: // Builds result string
300: StringBuffer result = new StringBuffer();
301: itr = path.listIterator(path.size());
302: while (itr.hasPrevious()) {
303: PathItem pathItem = itr.previous();
304: result.append(XPATH_SEPARATOR);
305:
306: if (pathItem.myAxiComp instanceof Attribute) {
307: result.append(XPATH_ATTR_INDICATOR);
308: }
309: if (pathItem.myNamespacePrefix != null
310: && pathItem.myNamespacePrefix.length() > 0) {
311: result.append(pathItem.myNamespacePrefix);
312: result.append(NAMESPACE_SEPARATOR);
313: }
314: //
315: result.append(pathItem.myLocalName);
316: //
317: if (pathItem.myPredicate != null) {
318: result.append(pathItem.myPredicate);
319: }
320: }
321: //
322: return result.toString();
323: }
324:
325: /**
326: * Tries obtain a prefix for the specified namespace URI.
327: * If the prefix isn't declared then registers a new unique prefix
328: * in the specified document.
329: *
330: * Returns the prefix. Prefix is always unique and should be not null.
331: */
332: public static String getUniquePrefix(AbstractDocumentComponent adc,
333: String namespaceUri, String prefixNamePattern) {
334: //
335: Map<String, String> prefixesMap = adc.getPrefixes();
336: Map<String, String> inversedPrefixesMap = getInverseMap(prefixesMap);
337: //
338: return getUniquePrefix(adc, prefixesMap, inversedPrefixesMap,
339: namespaceUri, prefixNamePattern);
340: }
341:
342: /**
343: *
344: * If the method creates a new prefix, it puts it to the both maps.
345: * So it can be used repeatedly without necessity of duplicative request
346: * of the prefixes' map.
347: *
348: */
349: private static String getUniquePrefix(
350: AbstractDocumentComponent adc,
351: Map<String, String> prefixesMap, // from prefix to namespace
352: Map<String, String> inversedPrefixesMap, // from namespace to prefix
353: String namespaceUri, String prefixNamePattern) {
354: //
355: String prefix = inversedPrefixesMap.get(namespaceUri);
356: if (prefix != null && prefix.length() > 0) {
357: return prefix;
358: }
359: //
360: int counter = 1;
361: String newPrefixCandidate;
362: while (true) {
363: newPrefixCandidate = prefixNamePattern + counter;
364: if (!prefixesMap.containsKey(newPrefixCandidate)) {
365: break;
366: }
367: counter++;
368: }
369: //
370: AbstractDocumentModel model = adc.getModel();
371: if (!model.isIntransaction()) {
372: model.startTransaction();
373: }
374: try {
375: adc.addPrefix(newPrefixCandidate, namespaceUri);
376: } finally {
377: if (model.isIntransaction()) {
378: model.endTransaction();
379: }
380: }
381: prefixesMap.put(newPrefixCandidate, namespaceUri);
382: inversedPrefixesMap.put(namespaceUri, newPrefixCandidate);
383: //
384: return newPrefixCandidate;
385: }
386:
387: public static class PathItem {
388: public AXIComponent myAxiComp;
389: public String myNamespacePrefix;
390: public String myNamespace;
391: public String myLocalName;
392: public String myPredicate;
393:
394: public PathItem(AXIComponent axiComp, String namespace,
395: String name, String predicate) {
396: myAxiComp = axiComp;
397: myNamespace = namespace;
398: myLocalName = name;
399: myPredicate = predicate;
400: }
401:
402: public QName constructQName() {
403: return new QName(myNamespace, myLocalName,
404: myNamespacePrefix);
405: }
406: }
407:
408: /**
409: * Constructs the inverse map where keys and values are change each others.
410: */
411: public static <A, B> Map<B, A> getInverseMap(Map<A, B> map) {
412: if (map == null) {
413: return null;
414: }
415: //
416: Map<B, A> resultMap = new HashMap<B, A>(map.size());
417: for (Map.Entry<A, B> entry : map.entrySet()) {
418: A key = entry.getKey();
419: B value = entry.getValue();
420: //
421: if (value != null && key != null) {
422: resultMap.put(value, key);
423: }
424: }
425: //
426: return resultMap;
427: }
428:
429: public static SchemaComponent getPartType(Part part) {
430: SchemaComponent result = null;
431: //
432: NamedComponentReference<GlobalElement> elementRef = part
433: .getElement();
434: if (elementRef != null) {
435: result = elementRef.get();
436: }
437: //
438: if (result == null) {
439: NamedComponentReference<? extends GlobalType> typeRef = part
440: .getType();
441: if (typeRef != null) {
442: result = typeRef.get();
443: }
444: }
445: //
446: return result;
447: }
448:
449: public static String getElementMultiplicityStr(Element element) {
450: if (element != null) {
451: String min = element.getMinOccurs();
452: String max = element.getMaxOccurs();
453: if (min.equals("1") && max.equals("1")) { // NOI18N
454: return null;
455: }
456: if (NumberBase.UNBOUNDED_STRING.equals(max)) {
457: max = "*"; // NOI18N
458: }
459: return "[" + min + ".." + max + "]"; // NOI18N
460: }
461: return null;
462: }
463:
464: public static BadgeModificator getElementBadge(Element element) {
465: if (element != null) {
466: String min = element.getMinOccurs();
467: String max = element.getMaxOccurs();
468: if (min.equals("1") && max.equals("1")) { // NOI18N
469: return BadgeModificator.SINGLE;
470: }
471: //
472: boolean isOptional = min.equals("0"); // NOI18N
473: //
474: boolean isRepeating = false;
475: if (NumberBase.UNBOUNDED_STRING.equals(max)) {
476: isRepeating = true;
477: } else {
478: try {
479: int maxInt = Integer.parseInt(max);
480: if (maxInt > 1) {
481: isRepeating = true;
482: }
483: } catch (NumberFormatException ex) {
484: // DO NOTHING HERE
485: }
486: }
487: //
488: if (isOptional && isRepeating) {
489: return BadgeModificator.OPTIONAL_REPEATING;
490: } else if (isOptional) {
491: return BadgeModificator.OPTIONAL;
492: } else if (isRepeating) {
493: return BadgeModificator.REPEATING;
494: }
495: }
496: return BadgeModificator.SINGLE;
497: }
498:
499: public static String getAttributeTooltip(Attribute attribute) {
500: if (attribute == null) {
501: return null;
502: }
503: //
504: String result;
505: AXIType type = attribute.getType();
506: String typeName = type != null ? type.getName() : null;
507: String isOptionalText = (attribute.getUse() == Use.OPTIONAL) ? "OPTIONAL"
508: : null; // NOI18N
509: //
510: if (typeName == null) {
511: result = attribute.getName(); // NOI18N
512: } else {
513: result = SoaUiUtil.getFormattedHtmlString(true,
514: new SoaUiUtil.TextChunk(attribute.getName()),
515: new SoaUiUtil.TextChunk(isOptionalText,
516: SoaUiUtil.HTML_GRAY),
517: new SoaUiUtil.TextChunk(typeName,
518: SoaUiUtil.HTML_GRAY));
519: }
520: //
521: return result;
522: }
523:
524: public static String getElementTooltip(Element element) {
525: if (element == null) {
526: return null;
527: }
528: //
529: String result;
530: AXIType type = element.getType();
531: String typeName = type != null ? type.getName() : null;
532: String multiplisity = getElementMultiplicityStr(element);
533: //
534: result = SoaUiUtil.getFormattedHtmlString(true,
535: new SoaUiUtil.TextChunk(element.getName()),
536: new SoaUiUtil.TextChunk(multiplisity,
537: SoaUiUtil.HTML_GRAY), new SoaUiUtil.TextChunk(
538: typeName, SoaUiUtil.HTML_GRAY));
539: //
540: return result;
541: }
542:
543: /**
544: * this function TRIES to access
545: * attributeFormDefault|elementFormDefault attribute in chema, defining the given element
546: * @type is element to check. may be Attribute or Element
547: * @returns true if it was found and equals to "unqualified""
548: **/
549: public static boolean isUnqualified(AXIComponent type) {
550: //ltl bit paranoic tests to avoid NPEs.
551: if (type == null) {
552: return false;
553: }
554:
555: if (type.isGlobal()) {
556: return false;
557: }
558:
559: SchemaComponent peer = type.getPeer();
560: if (peer == null) {
561: return false;
562: }
563:
564: Form form = null;
565: if (peer instanceof LocalElement) {
566: form = ((LocalElement) peer).getFormEffective();
567: } else if (peer instanceof LocalAttribute) {
568: form = ((LocalAttribute) peer).getFormEffective();
569:
570: }
571: return Form.UNQUALIFIED.equals(form);
572: }
573:
574: }
|