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: package org.netbeans.modules.bpel.model.impl.services;
020:
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.Collections;
024: import java.util.HashSet;
025: import java.util.IdentityHashMap;
026: import java.util.Iterator;
027: import java.util.LinkedList;
028: import java.util.Map;
029: import java.util.Set;
030:
031: import javax.xml.namespace.QName;
032:
033: import org.netbeans.modules.bpel.model.api.Variable;
034: import org.netbeans.modules.xml.xam.Named;
035: import org.netbeans.modules.xml.xpath.ext.XPathModelHelper;
036: import org.netbeans.modules.xml.xpath.ext.LocationStep;
037: import org.netbeans.modules.xml.xpath.ext.XPathCoreFunction;
038: import org.netbeans.modules.xml.xpath.ext.XPathCoreOperation;
039: import org.netbeans.modules.xml.xpath.ext.XPathException;
040: import org.netbeans.modules.xml.xpath.ext.XPathExpression;
041: import org.netbeans.modules.xml.xpath.ext.XPathExpressionPath;
042: import org.netbeans.modules.xml.xpath.ext.XPathExtensionFunction;
043: import org.netbeans.modules.xml.xpath.ext.XPathLocationPath;
044: import org.netbeans.modules.xml.xpath.ext.XPathModel;
045: import org.netbeans.modules.xml.xpath.ext.XPathModelFactory;
046: import org.netbeans.modules.xml.xpath.ext.XPathNumericLiteral;
047: import org.netbeans.modules.xml.xpath.ext.XPathOperationOrFuntion;
048: import org.netbeans.modules.xml.xpath.ext.XPathPredicateExpression;
049: import org.netbeans.modules.xml.xpath.ext.XPathStringLiteral;
050: import org.netbeans.modules.xml.xpath.ext.XPathVariableReference;
051: import org.netbeans.modules.xml.xpath.ext.visitor.XPathModelTracerVisitor;
052: import org.netbeans.modules.xml.xpath.ext.visitor.XPathVisitor;
053:
054: /**
055: * @author ads
056: * @author nk160297
057: */
058: public final class ExpressionUpdater {
059:
060: public static class ExpressionException extends Exception {
061:
062: private static final long serialVersionUID = -6309073089869606561L;
063:
064: /**
065: * {@inheritDoc}
066: */
067: public ExpressionException() {
068: super ();
069: }
070:
071: /**
072: * {@inheritDoc}
073: */
074: public ExpressionException(String message) {
075: super (message);
076: }
077:
078: /**
079: * {@inheritDoc}
080: */
081: public ExpressionException(String message, Throwable cause) {
082: super (message, cause);
083: }
084:
085: /**
086: * {@inheritDoc}
087: */
088: public ExpressionException(Throwable cause) {
089: super (cause);
090: }
091:
092: }
093:
094: public static class InvalidExpressionException extends
095: ExpressionException {
096:
097: private static final long serialVersionUID = -461547631006192178L;
098:
099: /**
100: * {@inheritDoc}
101: */
102: public InvalidExpressionException() {
103: super ();
104: }
105:
106: /**
107: * {@inheritDoc}
108: */
109: public InvalidExpressionException(String message) {
110: super (message);
111: }
112:
113: /**
114: * {@inheritDoc}
115: */
116: public InvalidExpressionException(String message,
117: Throwable cause) {
118: super (message, cause);
119: }
120:
121: /**
122: * {@inheritDoc}
123: */
124: public InvalidExpressionException(Throwable cause) {
125: super (cause);
126: }
127:
128: }
129:
130: private ExpressionUpdater() {
131: myFactories.add(new VariableReferenceFactory());
132: //myFactories.add( new PartReferenceFactory() );
133: }
134:
135: public static ExpressionUpdater getInstance() {
136: return INSTANCE;
137: }
138:
139: /**
140: * Method returns true if <code>component</code> is present in
141: * <code>expression</code>.
142: * @param expression Subject expression.
143: * @param component Component that will be trying to find.
144: * @return true if component is found in expression.
145: */
146: public boolean isPresent(String expression, Named component) {
147: if (expression == null || expression.length() == 0) {
148: return false;
149: }
150: for (RefactoringReferenceFactory factory : myFactories) {
151: if (factory.isApplicable(component)) {
152: return factory.isPresent(expression, component);
153: }
154: }
155: return false;
156: }
157:
158: /**
159: * Update entrance of <code>component</code> in <code>expression</code>
160: * with new <code>component</code> name.
161: * @param expression Subject expression.
162: * @param component Component that will be trying to find for update.
163: * @param newName New name of component.
164: * @return Updated expression or null if it was not updated.
165: */
166: public String update(String expression, Named component,
167: String newName) {
168: if (expression == null || expression.length() == 0) {
169: return null;
170: }
171: for (RefactoringReferenceFactory factory : myFactories) {
172: if (factory.isApplicable(component)) {
173: return factory.update(expression, component, newName);
174: }
175: }
176: return null;
177: }
178:
179: /**
180: * @param expression Subject expression.
181: * @return collection names of found variables.
182: */
183: @SuppressWarnings("unchecked")
184: public Collection<String> getUsedVariables(String expression) {
185: if (expression == null || expression.length() == 0) {
186: return Collections.EMPTY_LIST;
187: } else {
188: try {
189: XPathModel model = XPathModelHelper.getInstance()
190: .newXPathModel();
191: XPathExpression exp = model.parseExpression(expression);
192: FindVaribleVisitor visitor = new FindVaribleVisitor();
193: exp.accept(visitor);
194: return visitor.getVariables();
195: } catch (XPathException e) {
196: return Collections.EMPTY_LIST;
197: }
198: }
199: }
200:
201: private static ExpressionUpdater INSTANCE = new ExpressionUpdater();
202:
203: private Set<RefactoringReferenceFactory> myFactories = new HashSet<RefactoringReferenceFactory>();
204:
205: }
206:
207: interface RefactoringReferenceFactory {
208:
209: boolean isApplicable(Named component);
210:
211: boolean isPresent(String expression, Named component);
212:
213: String update(String expression, Named component, String newName);
214:
215: }
216:
217: class VariableReferenceFactory implements RefactoringReferenceFactory {
218:
219: /* (non-Javadoc)
220: * @see org.netbeans.modules.bpel.model.impl.RefactoringReferenceFactory#isApplicable(org.netbeans.modules.xml.xam.Named)
221: */
222: public boolean isApplicable(Named component) {
223: return component instanceof Variable
224: || component instanceof Named;//todo
225: }
226:
227: /* (non-Javadoc)
228: * @see org.netbeans.modules.bpel.model.impl.RefactoringReferenceFactory#isPresent(java.lang.String, org.netbeans.modules.xml.xam.Named)
229: */
230: public boolean isPresent(String expression, Named component) {
231: XPathModel model = XPathModelHelper.getInstance()
232: .newXPathModel();
233: if (expression == null || component.getName() == null) {
234: return false;
235: }
236: try {
237: XPathExpression exp = model.parseExpression(expression);
238:
239: FindVaribleVisitor visitor = new FindVaribleVisitor(
240: component.getName());
241:
242: exp.accept(visitor);
243:
244: return visitor.isFound();
245: } catch (XPathException e) {
246: // in the case when we cannot parse expression we return false
247: return false;
248: }
249: }
250:
251: /* (non-Javadoc)
252: * @see org.netbeans.modules.bpel.model.impl.RefactoringReferenceFactory#update(java.lang.String, org.netbeans.modules.xml.xam.Named, java.lang.String)
253: */
254: public String update(String expression, Named component,
255: String newName) {
256: XPathModel model = XPathModelHelper.getInstance()
257: .newXPathModel();
258: if (expression == null || component.getName() == null) {
259: return null;
260: }
261: try {
262: XPathExpression exp = model.parseExpression(expression);
263: UpdateVaribleVisitor visitor = new UpdateVaribleVisitor(
264: component.getName(), newName, exp);
265: exp.accept(visitor);
266: // FIX for #IZ80076
267: String ret = visitor.getExpressionString();
268: if (expression.trim().equals(ret)) {
269: // we don't want to update not changed expression.
270: return null;
271: }
272: return ret;
273: } catch (XPathException e) {
274: // in the case when we cannot parse expression we return false
275: return null;
276: }
277: }
278:
279: }
280:
281: /**
282: * It determine presence for either just variable or variable and part in expression.
283: *
284: * @author ads
285: *
286: */
287: class FindVaribleVisitor extends XPathModelTracerVisitor {
288:
289: /**
290: * Visitor for finding all alvailible variables.
291: */
292: FindVaribleVisitor() {
293: myFoundVars = new LinkedList<String>();
294: }
295:
296: /**
297: * Visitor for finding just variable with specified name.
298: */
299: FindVaribleVisitor(String name) {
300: this ();
301: myName = name;
302: }
303:
304: /**
305: * Visitor for finding variable and part .
306: */
307: FindVaribleVisitor(String varName, String partName) {
308: this (varName);
309: assert partName != null;
310: myPartName = partName;
311: }
312:
313: @Override
314: public void visit(XPathVariableReference variableRef) {
315: QName qName = variableRef.getVariableName();
316: if (qName != null
317: //&& BpelEntity.BUSINESS_PROCESS_NS_URI.equals( qName.getNamespaceURI()) TODO !
318: ) {
319: String local = qName.getLocalPart();
320:
321: if (local == null) {
322: return;
323: }
324: int index = local.indexOf(".");
325: /*
326: * trying to devide variable in two part : bpel variable name and part.
327: * ( the part could be absent )
328: */
329:
330: if (index < 0) {
331: myFoundVars.add(local); // FIX for IZ79861
332: isFound = local.equals(myName);
333: if (isFound && myPartName != null) {
334: isFound = false; // string doesn't contain "." so there is no part
335: }
336: } else {
337: String varName = local.substring(0, index);
338: myFoundVars.add(varName); // FIX for IZ79861
339: String part = (index < (local.length() - 1)) ? local
340: .substring(index + 1) : null;
341: isFound = varName.equals(myName);
342:
343: if (isFound && myPartName != null) {
344: isFound = myPartName.equals(part);
345: }
346: }
347: }
348: }
349:
350: public Collection<String> getVariables() {
351: return myFoundVars;
352: }
353:
354: boolean isFound() {
355: return isFound;
356: }
357:
358: private String myName;
359:
360: private String myPartName;
361:
362: private boolean isFound;
363:
364: private Collection<String> myFoundVars;
365: }
366:
367: /**
368: * It updates expression with new value for either just variable or part with
369: * specified variable.
370: * @author ads
371: *
372: */
373: class UpdateVaribleVisitor extends XPathModelTracerVisitor {
374:
375: UpdateVaribleVisitor(String name, String newName,
376: XPathExpression expression) {
377: myName = name;
378: myNewName = newName;
379: myMap = new IdentityHashMap<XPathExpression, XPathExpression>();
380: myExpression = expression;
381: myUpdater = new ExpressionUpdaterVisitor(myMap, expression);
382: }
383:
384: UpdateVaribleVisitor(String varName, String partName,
385: String newName, XPathExpression expression) {
386: this (varName, newName, expression);
387: assert partName != null;
388: myPartName = partName;
389: }
390:
391: public void visit(LocationStep locationStep) {
392: super .visit(locationStep);
393: updateExpression(locationStep);
394: }
395:
396: public void visit(XPathCoreFunction coreFunction) {
397: visitChildren(coreFunction);
398: updateExpression(coreFunction);
399: }
400:
401: public void visit(XPathCoreOperation coreOperation) {
402: visitChildren(coreOperation);
403: updateExpression(coreOperation);
404: }
405:
406: public void visit(XPathExpressionPath expressionPath) {
407: super .visit(expressionPath);
408: updateExpression(expressionPath);
409: }
410:
411: public void visit(XPathExtensionFunction extensionFunction) {
412: visitChildren(extensionFunction);
413: updateExpression(extensionFunction);
414: }
415:
416: public void visit(XPathLocationPath locationPath) {
417: super .visit(locationPath);
418: updateExpression(locationPath);
419: }
420:
421: @Override
422: public void visit(XPathVariableReference variableRef) {
423: QName qName = variableRef.getVariableName();
424: if (qName != null
425: //&& BpelEntity.BUSINESS_PROCESS_NS_URI.equals( qName.getNamespaceURI()) TODO !
426: ) {
427: String local = qName.getLocalPart();
428: if (local == null) {
429: return;
430: }
431: int index = local.indexOf(".");
432: /*
433: * trying to devide variable in two part : bpel variable name and part.
434: * ( the part could be absent )
435: */
436: XPathModelFactory factory = myExpression.getModel()
437: .getFactory();
438: if (index < 0) {
439: if (local.equals(myName) && myPartName == null) {
440: XPathVariableReference newRef = factory
441: .newXPathVariableReference(new QName(
442: myNewName));
443: myMap.put(variableRef, newRef);
444: }
445: } else {
446: String varName = local.substring(0, index);
447: String part = (index < (local.length() - 1)) ? local
448: .substring(index + 1) : null;
449:
450: if (!varName.equals(myName)) {
451: return;
452: }
453:
454: String newName;
455: if (myPartName != null) {
456: newName = myName + "." + myNewName;
457: } else {
458: newName = myNewName + "." + part;
459: }
460: XPathVariableReference newRef = factory
461: .newXPathVariableReference(new QName(newName));
462: myMap.put(variableRef, newRef);
463: }
464: }
465: updateExpression(variableRef);
466: }
467:
468: /* (non-Javadoc)
469: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathStringLiteral)
470: */
471: public void visit(XPathStringLiteral stringLiteral) {
472: updateExpression(stringLiteral);
473: }
474:
475: /* (non-Javadoc)
476: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathNumericLiteral)
477: */
478: public void visit(XPathNumericLiteral numericLiteral) {
479: updateExpression(numericLiteral);
480: }
481:
482: /* (non-Javadoc)
483: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathPredicateExpression)
484: */
485: public void visit(XPathPredicateExpression predicateExpression) {
486: updateExpression(predicateExpression);
487: }
488:
489: // FIX for #IZ80076
490: String getExpressionString() {
491: if (myExpression == null) {
492: return null;
493: }
494: return myExpression.getExpressionString();
495: }
496:
497: // FIX for #IZ80076
498: private void updateExpression(XPathExpression expression) {
499: expression.accept(myUpdater);
500: myExpression = myUpdater.getExpression();
501: }
502:
503: private String myName;
504:
505: private String myPartName;
506:
507: private String myNewName;
508:
509: private ExpressionUpdaterVisitor myUpdater;
510:
511: private Map<XPathExpression, XPathExpression> myMap;
512:
513: private XPathExpression myExpression;
514:
515: }
516:
517: /**
518: * This is generic children updater.
519: * It is constracted with Map that contains in key old expression,
520: * in value - new expression . This visitor will change
521: * old expression in parent to new expression and remove key from Map.
522: *
523: * @author ads
524: *
525: */
526: class ExpressionUpdaterVisitor implements XPathVisitor {
527:
528: ExpressionUpdaterVisitor(
529: Map<XPathExpression, XPathExpression> oldNewMap,
530: XPathExpression expression) {
531: myMap = oldNewMap;
532: myExpression = expression;
533: }
534:
535: public void visit(LocationStep locationStep) {
536: updateExpression(locationStep);
537: XPathPredicateExpression[] expressions = locationStep
538: .getPredicates();
539: boolean hasChanges = false;
540: if (expressions != null) {
541: int i = 0;
542: for (XPathPredicateExpression expression : expressions) {
543: XPathExpression expr = myMap.remove(expression);
544: if (expr != null) {
545: hasChanges = true;
546: expressions[i] = (XPathPredicateExpression) expr;
547: }
548: i++;
549: }
550: }
551: if (hasChanges) {
552: locationStep.setPredicates(expressions);
553: }
554: }
555:
556: public void visit(XPathCoreFunction coreFunction) {
557: updateExpression(coreFunction);
558: visitChildren(coreFunction);
559: }
560:
561: public void visit(XPathCoreOperation coreOperation) {
562: updateExpression(coreOperation);
563: visitChildren(coreOperation);
564: }
565:
566: public void visit(XPathExpressionPath expressionPath) {
567: updateExpression(expressionPath);
568: XPathExpression expression = expressionPath.getRootExpression();
569: XPathExpression expr = myMap.remove(expression);
570: if (expr != null) {
571: expressionPath.setRootExpression(expr);
572: }
573:
574: boolean hasChanges = false;
575: LocationStep[] steps = expressionPath.getSteps();
576: if (steps != null) {
577: int i = 0;
578: for (LocationStep step : steps) {
579: expr = myMap.remove(step);
580: if (expr != null) {
581: hasChanges = true;
582: steps[i] = (LocationStep) expr;
583: }
584: i++;
585: }
586: }
587: if (hasChanges) {
588: expressionPath.setSteps(steps);
589: }
590: }
591:
592: public void visit(XPathExtensionFunction extensionFunction) {
593: updateExpression(extensionFunction);
594: visitChildren(extensionFunction);
595: }
596:
597: public void visit(XPathLocationPath locationPath) {
598: updateExpression(locationPath);
599: LocationStep[] steps = locationPath.getSteps();
600: boolean hasChanges = false;
601: if (steps != null) {
602: int i = 0;
603: for (LocationStep step : steps) {
604: XPathExpression expr = myMap.remove(step);
605: if (expr != null) {
606: hasChanges = true;
607: steps[i] = (LocationStep) expr;
608: }
609: i++;
610: }
611: }
612: if (hasChanges) {
613: locationPath.setSteps(steps);
614: }
615: }
616:
617: /* (non-Javadoc)
618: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathStringLiteral)
619: */
620: public void visit(XPathStringLiteral stringLiteral) {
621: /*XPathExpression expression = myMap.remove( stringLiteral );
622: if (expression != null ) {
623: stringLiteral.setValue( ((XPathStringLiteral)expression).getValue() );
624: }*/
625: updateExpression(stringLiteral);
626: }
627:
628: /* (non-Javadoc)
629: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathNumericLiteral)
630: */
631: public void visit(XPathNumericLiteral numericLiteral) {
632: /*XPathExpression expression = myMap.remove( numericLiteral );
633: if (expression != null ) {
634: numericLiteral.setValue( ((XPathNumericLiteral)expression).getValue() );
635: }*/
636: updateExpression(numericLiteral);
637: }
638:
639: /* (non-Javadoc)
640: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathVariableReference)
641: */
642: public void visit(XPathVariableReference variableReference) {
643: updateExpression(variableReference);
644: }
645:
646: /* (non-Javadoc)
647: * @see org.netbeans.modules.xml.xpath.visitor.XPathVisitor#visit(org.netbeans.modules.xml.xpath.XPathPredicateExpression)
648: */
649: public void visit(XPathPredicateExpression predicateExpression) {
650: updateExpression(predicateExpression);
651: }
652:
653: XPathExpression getExpression() {
654: return myExpression;
655: }
656:
657: protected void visitChildren(XPathOperationOrFuntion expr) {
658: Collection<XPathExpression> children = expr.getChildren();
659: Collection<XPathExpression> newChildren = null;
660: boolean hasChanges = false;
661: if (children != null) {
662: newChildren = new ArrayList<XPathExpression>(children
663: .size());
664: Iterator<XPathExpression> it = children.iterator();
665: while (it.hasNext()) {
666: XPathExpression child = it.next();
667: XPathExpression expression = myMap.remove(child);
668: if (expression != null) {
669: hasChanges = true;
670: newChildren.add(expression); // Fix for IZ#80079
671: } else {
672: newChildren.add(child);
673: }
674: }
675: }
676: if (hasChanges) {
677: expr.clearChildren();
678: for (XPathExpression expression : newChildren) {
679: expr.addChild(expression);
680: }
681: }
682: }
683:
684: private void updateExpression(XPathExpression expression) {
685: XPathExpression updated = myMap.get(expression);
686: /*
687: * only when ALL expression should be changed ( because
688: * it doesn't have mutation method ) we replace
689: * this expression to new from map. This could
690: * be applicable ONLY for expression that
691: * is oroginal expression from which we start.
692: * Any subexpression will be handled as child in
693: * appropriate container.
694: * This is FIX for #IZ80076
695: */
696: if (updated != null && myExpression == expression) {
697: myExpression = updated;
698: }
699: }
700:
701: private Map<XPathExpression, XPathExpression> myMap = new IdentityHashMap<XPathExpression, XPathExpression>();
702:
703: // this need when expression itself needs to be changed.
704: private XPathExpression myExpression;
705:
706: }
|