001 /*
002 * Copyright (c) 2005, 2009 IBM Corporation, Embarcadero Technologies, 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 - initial API and implementation
010 * Kenn Hussey (Embarcadero Technologies) - 204200, 247980
011 *
012 * $Id: UML2Util.java,v 1.37 2009/03/13 20:41:16 jbruck Exp $
013 */
014 package org.eclipse.uml2.common.util;
015
016 import java.io.IOException;
017 import java.io.InputStream;
018 import java.util.ArrayList;
019 import java.util.Collection;
020 import java.util.Collections;
021 import java.util.HashMap;
022 import java.util.Iterator;
023 import java.util.List;
024 import java.util.Locale;
025 import java.util.Map;
026 import java.util.MissingResourceException;
027 import java.util.PropertyResourceBundle;
028 import java.util.ResourceBundle;
029 import java.util.WeakHashMap;
030
031 import org.eclipse.core.runtime.Platform;
032 import org.eclipse.emf.common.EMFPlugin;
033 import org.eclipse.emf.common.util.AbstractTreeIterator;
034 import org.eclipse.emf.common.util.DiagnosticChain;
035 import org.eclipse.emf.common.util.TreeIterator;
036 import org.eclipse.emf.common.util.URI;
037 import org.eclipse.emf.common.util.UniqueEList;
038 import org.eclipse.emf.ecore.EAnnotation;
039 import org.eclipse.emf.ecore.EClass;
040 import org.eclipse.emf.ecore.EClassifier;
041 import org.eclipse.emf.ecore.EDataType;
042 import org.eclipse.emf.ecore.EEnum;
043 import org.eclipse.emf.ecore.EModelElement;
044 import org.eclipse.emf.ecore.ENamedElement;
045 import org.eclipse.emf.ecore.EObject;
046 import org.eclipse.emf.ecore.EReference;
047 import org.eclipse.emf.ecore.EStructuralFeature;
048 import org.eclipse.emf.ecore.ETypedElement;
049 import org.eclipse.emf.ecore.EValidator;
050 import org.eclipse.emf.ecore.EcoreFactory;
051 import org.eclipse.emf.ecore.EcorePackage;
052 import org.eclipse.emf.ecore.InternalEObject;
053 import org.eclipse.emf.ecore.resource.Resource;
054 import org.eclipse.emf.ecore.resource.ResourceSet;
055 import org.eclipse.emf.ecore.resource.URIConverter;
056 import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl;
057 import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
058 import org.eclipse.emf.ecore.util.EcoreSwitch;
059 import org.eclipse.emf.ecore.util.EcoreUtil;
060 import org.eclipse.emf.ecore.xml.type.util.XMLTypeUtil;
061 import org.eclipse.uml2.common.CommonPlugin;
062 import org.osgi.framework.Bundle;
063
064 /**
065 * Utilities for working with EMF-based objects and resources.
066 *
067 * @since 1.2
068 */
069 public class UML2Util {
070
071 protected UML2Util() {
072 super();
073 }
074
075 /**
076 * An interface for classes that can determine matches based on some
077 * criteria.
078 */
079 public static interface EObjectMatcher {
080
081 /**
082 * Determines whether the specified object matches some criteria.
083 *
084 * @param eObject
085 * The candidate object.
086 * @return <code>true</code> if the object meets the criteria;
087 * <code>false</code> otherwise.
088 */
089 public boolean matches(EObject eObject);
090
091 }
092
093 /**
094 * A matcher that determines matches based on the class of candidate
095 * objects.
096 */
097 public static class EClassMatcher
098 implements EObjectMatcher {
099
100 protected final EObject eObject;
101
102 /**
103 * Constructs a new class matcher based on the specified object.
104 *
105 * @param eObject
106 * The object whose class is to be used as a match criterion.
107 */
108 public EClassMatcher(EObject eObject) {
109 super();
110
111 this.eObject = eObject;
112 }
113
114 public boolean matches(EObject otherEObject) {
115 return eObject == null
116 ? false
117 : (otherEObject == null
118 ? false
119 : eObject.eClass() == otherEObject.eClass());
120 }
121 }
122
123 /**
124 * A matcher that determines matches based on the class of, and the value
125 * for a specific structural feature held by, candidate objects.
126 */
127 public static class EStructuralFeatureMatcher
128 extends EClassMatcher {
129
130 protected final EStructuralFeature eStructuralFeature;
131
132 /**
133 * Constructs a new structural feature matcher based on the specified
134 * object.
135 *
136 * @param eObject
137 * The object whose class is to be used as a match criterion.
138 * @param eStructuralFeature
139 * The structural feature whose value is to be used as a
140 * match criterion.
141 */
142 public EStructuralFeatureMatcher(EObject eObject,
143 EStructuralFeature eStructuralFeature) {
144 super(eObject);
145
146 this.eStructuralFeature = eStructuralFeature;
147 }
148
149 @Override
150 public boolean matches(EObject otherEObject) {
151 return super.matches(otherEObject)
152 && safeEquals(eObject.eGet(eStructuralFeature), otherEObject
153 .eGet(eStructuralFeature));
154 }
155 }
156
157 /**
158 * An interface for classes that can convert objects to another
159 * representation.
160 */
161 public static interface Converter {
162
163 /**
164 * Converts the specified objects using the specified options, reporting
165 * problems to the specified diagnostics, within the specified context.
166 *
167 * @param eObjects
168 * The objects to be converted.
169 * @param options
170 * The options to use.
171 * @param diagnostics
172 * The chain of diagnostics to which problems are to be
173 * appended.
174 * @param context
175 * The cache of context-specific information.
176 * @return Another representation of the objects.
177 */
178 Collection<? extends EObject> convert(
179 Collection<? extends EObject> eObjects,
180 Map<String, String> options, DiagnosticChain diagnostics,
181 Map<Object, Object> context);
182
183 }
184
185 /**
186 * The abstract parent of classes that can provide a textual representation
187 * of objects, classes, and features.
188 */
189 public static abstract class QualifiedTextProvider {
190
191 /**
192 * Retrieves a textual representation of the specified object.
193 *
194 * @param eObject
195 * The object for which to retrieve text.
196 * @return A textual representation of the object.
197 */
198 public String getText(EObject eObject) {
199
200 return eObject instanceof ENamedElement
201 ? ((ENamedElement) eObject).getName()
202 : EMPTY_STRING;
203 }
204
205 /**
206 * Retrieves the string used to separate segments of qualified text.
207 *
208 * @return The separator to be used.
209 */
210 public abstract String getSeparator();
211
212 /**
213 * Retrieves a textual representation of the specified structural
214 * feature.
215 *
216 * @param eStructuralFeature
217 * The feature for which to retrieve text.
218 * @return A textual representation of the feature.
219 */
220 public String getFeatureText(EStructuralFeature eStructuralFeature) {
221 return eStructuralFeature.getName();
222 }
223
224 /**
225 * Retrieves a textual representation of the specified object's class.
226 *
227 * @param eObject
228 * The object for which to retrieve text.
229 * @return A textual representation of the object's class.
230 */
231 public String getClassText(EObject eObject) {
232 return eObject.eClass().getName();
233 }
234
235 }
236
237 /**
238 * The default URI converter for resource bundle look-ups.
239 */
240 protected static final URIConverter DEFAULT_URI_CONVERTER = new ExtensibleURIConverterImpl();
241
242 /**
243 * A cache of resource bundles.
244 */
245 protected static final Map<Resource, Map<Locale, ResourceBundle>> RESOURCE_BUNDLES = Collections
246 .synchronizedMap(new WeakHashMap<Resource, Map<Locale, ResourceBundle>>());
247
248 /**
249 * The empty string.
250 */
251 public static final String EMPTY_STRING = ""; //$NON-NLS-1$
252
253 /**
254 * The platform line separator.
255 */
256 protected static final String LINE_SEPARATOR = System.getProperties()
257 .getProperty("line.separator"); //$NON-NLS-1$
258
259 /**
260 * The scheme for platform URIs.
261 */
262 public static final String URI_SCHEME_PLATFORM = "platform"; //$NON-NLS-1$
263
264 /**
265 * The first segment for platform plugin URIs.
266 */
267 public static final String URI_SEGMENT_PLUGIN = "plugin"; //$NON-NLS-1$
268
269 /**
270 * The first segment for platform resource URIs.
271 */
272 public static final String URI_SEGMENT_RESOURCE = "resource"; //$NON-NLS-1$
273
274 /**
275 * The standard extension for properties files.
276 */
277 public static final String PROPERTIES_FILE_EXTENSION = "properties"; //$NON-NLS-1$
278
279 /**
280 * Retrieves the candidate resource bundle URIs based on the specified base
281 * URI and base segment in the specified locale.
282 *
283 * @param baseURI
284 * The base URI (i.e. without the last segment) for the candidate
285 * resource bundle URIs.
286 * @param locale
287 * The locale within which to base the candidate resource bundle
288 * URIs.
289 * @param baseSegment
290 * The base segment (i.e. the last segment without the extension)
291 * for the candidate resource bundle URIs.
292 * @return The candidate resource bundle URIs with the base URI and base
293 * segment in the locale.
294 */
295 protected static List<URI> getResourceBundleURIs(URI baseURI,
296 Locale locale, String baseSegment) {
297 List<URI> resourceBundleURIs = new ArrayList<URI>();
298 String language = locale.getLanguage();
299
300 if (language.length() > 0) {
301 baseSegment += ('_' + language);
302 resourceBundleURIs.add(0, baseURI.appendSegment(baseSegment)
303 .appendFileExtension(PROPERTIES_FILE_EXTENSION));
304
305 String country = locale.getCountry();
306
307 if (country.length() > 0) {
308 baseSegment += ('_' + country);
309 resourceBundleURIs.add(0, baseURI.appendSegment(baseSegment)
310 .appendFileExtension(PROPERTIES_FILE_EXTENSION));
311
312 String variant = locale.getVariant();
313
314 if (variant.length() > 0) {
315 baseSegment += ('_' + variant);
316 resourceBundleURIs.add(0, baseURI
317 .appendSegment(baseSegment).appendFileExtension(
318 PROPERTIES_FILE_EXTENSION));
319 }
320 }
321 }
322
323 return resourceBundleURIs;
324 }
325
326 /**
327 * Retrieves the candidate resource bundle URIs for the specified URI in the
328 * specified locale (if specified).
329 *
330 * @param uri
331 * The URI upon which to base the candidate resource bundle URIs.
332 * @param locale
333 * The locale within which to base the candidate resource bundle
334 * URIs, or <code>null</code>.
335 * @return The candidate resource bundle URIs for the URI in the locale (if
336 * specified).
337 */
338 protected static List<URI> getResourceBundleURIs(URI uri, Locale locale) {
339 List<URI> resourceBundleURIs = new ArrayList<URI>();
340 URI baseURI = uri.trimSegments(1);
341 String baseSegment = uri.trimFileExtension().lastSegment();
342
343 if (baseSegment != null) {
344 resourceBundleURIs.add(baseURI.appendSegment(baseSegment)
345 .appendFileExtension(PROPERTIES_FILE_EXTENSION));
346
347 if (locale != null) {
348 Locale defaultLocale = Locale.getDefault();
349
350 resourceBundleURIs.addAll(0, getResourceBundleURIs(baseURI,
351 defaultLocale, baseSegment));
352
353 if (!locale.equals(defaultLocale)) {
354 resourceBundleURIs.addAll(0, getResourceBundleURIs(baseURI,
355 locale, baseSegment));
356 }
357 }
358 }
359
360 return resourceBundleURIs;
361 }
362
363 /**
364 * Retrieves the (cached) resource bundle for the specified object in the
365 * specified locale (if specified).
366 *
367 * @param eObject
368 * The object for which to retrieve the resource bundle.
369 * @param locale
370 * The locale in which to retrieve the resource bundle, or
371 * <code>null</code>.
372 * @return The resource bundle for the object in the locale (if specified).
373 */
374 protected static ResourceBundle getResourceBundle(EObject eObject,
375 Locale locale) {
376 Resource resource = eObject.eResource();
377
378 if (resource != null) {
379 Map<Locale, ResourceBundle> resourceBundles = RESOURCE_BUNDLES
380 .get(resource);
381
382 if (resourceBundles == null) {
383 RESOURCE_BUNDLES.put(resource, resourceBundles = Collections
384 .synchronizedMap(new HashMap<Locale, ResourceBundle>()));
385 }
386
387 if (!resourceBundles.containsKey(locale)) {
388 ResourceSet resourceSet = resource.getResourceSet();
389 URIConverter uriConverter = resourceSet == null
390 ? DEFAULT_URI_CONVERTER
391 : resourceSet.getURIConverter();
392
393 URI uri = resource.getURI();
394 List<URI> resourceBundleURIs = getResourceBundleURIs(uri,
395 locale);
396
397 if (EMFPlugin.IS_ECLIPSE_RUNNING) {
398 URI normalizedURI = uriConverter.normalize(uri);
399 int segmentCount = normalizedURI.segmentCount();
400
401 if (URI_SCHEME_PLATFORM.equals(normalizedURI.scheme())
402 && segmentCount > 2
403 && URI_SEGMENT_PLUGIN.equals(normalizedURI.segment(0))) {
404
405 Bundle bundle = Platform.getBundle(normalizedURI
406 .segment(1));
407
408 if (bundle != null) {
409 Bundle[] fragments = Platform.getFragments(bundle);
410
411 if (fragments != null) {
412 String[] trailingSegments = normalizedURI
413 .segmentsList().subList(2, segmentCount)
414 .toArray(new String[]{});
415
416 for (int f = 0; f < fragments.length; f++) {
417 resourceBundleURIs.addAll(0,
418 getResourceBundleURIs(normalizedURI
419 .trimSegments(segmentCount - 1)
420 .appendSegment(
421 fragments[f].getSymbolicName())
422 .appendSegments(trailingSegments),
423 locale));
424 }
425 }
426 }
427 }
428 }
429
430 ResourceBundle resourceBundle = null;
431
432 for (Iterator<URI> rbu = resourceBundleURIs.iterator(); rbu
433 .hasNext();) {
434
435 try {
436 InputStream inputStream = uriConverter
437 .createInputStream(rbu.next());
438 try {
439 resourceBundle = new PropertyResourceBundle(
440 inputStream);
441 } finally {
442 inputStream.close();
443 }
444 break;
445 } catch (IOException ioe) {
446 // ignore
447 }
448 }
449
450 resourceBundles.put(locale, resourceBundle);
451 }
452
453 return resourceBundles.get(locale);
454 }
455
456 return null;
457 }
458
459 /**
460 * Retrieves the (cached) resource bundle for the specified object,
461 * localized in the default locale if indicated.
462 *
463 * @param eObject
464 * The object for which to retrieve the resource bundle.
465 * @param localize
466 * Whether to retrieve the resource bundle based on (the default)
467 * locale.
468 * @return The resource bundle for the object (in the default locale).
469 */
470 protected static ResourceBundle getResourceBundle(EObject eObject,
471 boolean localize) {
472 return getResourceBundle(eObject, localize
473 ? Locale.getDefault()
474 : null);
475 }
476
477 /**
478 * Retrieves a string for the specified object, localized if indicated.
479 *
480 * @param eObject
481 * The object for which to retrieve a (localized) string.
482 * @param key
483 * The key in the resource bundle.
484 * @param defaultString
485 * The string to return if no string for the given key can be
486 * found.
487 * @param localize
488 * Whether the string should be localized.
489 * @return The (localized) string.
490 */
491 protected static String getString(EObject eObject, String key,
492 String defaultString, boolean localize) {
493 String string = defaultString;
494
495 if (eObject != null) {
496
497 try {
498 ResourceBundle resourceBundle = getResourceBundle(eObject,
499 localize);
500
501 if (resourceBundle != null) {
502 string = resourceBundle.getString(key);
503 }
504 } catch (MissingResourceException mre) {
505 // ignore
506 }
507 }
508
509 return string;
510 }
511
512 /**
513 * Retrieves a qualified textual representation of the specified object
514 * using the specified qualified text provider.
515 *
516 * @param eObject
517 * The object for which to retrieve qualified text.
518 * @param qualifiedTextProvider
519 * The provider to be used.
520 * @return A qualified textual representation of the object.
521 */
522 public static String getQualifiedText(EObject eObject,
523 QualifiedTextProvider qualifiedTextProvider) {
524
525 return getQualifiedText(eObject, qualifiedTextProvider,
526 new StringBuffer()).toString();
527 }
528
529 protected static StringBuffer getQualifiedText(EObject eObject,
530 QualifiedTextProvider qualifiedTextProvider,
531 StringBuffer qualifiedText) {
532
533 EObject eContainer = eObject == null
534 ? null
535 : eObject.eContainer();
536
537 if (eContainer != null) {
538 getQualifiedText(eContainer, qualifiedTextProvider, qualifiedText);
539
540 if (qualifiedText.length() > 0) {
541 qualifiedText.append(qualifiedTextProvider.getSeparator());
542 }
543 }
544
545 return getQualifiedTextSegment(eObject, qualifiedTextProvider,
546 qualifiedText);
547 }
548
549 protected static StringBuffer getQualifiedTextSegment(EObject eObject,
550 QualifiedTextProvider qualifiedTextProvider,
551 StringBuffer qualifiedText) {
552
553 String text = qualifiedTextProvider.getText(eObject);
554
555 if (!isEmpty(text)) {
556 return qualifiedText.append(text);
557 } else if (eObject == null) {
558 return qualifiedText.append(String.valueOf(eObject));
559 }
560
561 qualifiedText.append('{');
562
563 EStructuralFeature eContainingFeature = eObject.eContainingFeature();
564
565 if (eContainingFeature != null) {
566 qualifiedText.append(qualifiedTextProvider
567 .getFeatureText(eContainingFeature));
568
569 if (eContainingFeature.isMany()) {
570 qualifiedText.append(' ');
571
572 List<?> list = (List<?>) eObject.eContainer().eGet(
573 eContainingFeature, false);
574
575 qualifiedText.append('[');
576 qualifiedText.append(list.indexOf(eObject));
577 qualifiedText.append(']');
578 }
579
580 qualifiedText.append(' ');
581 }
582
583 qualifiedText.append(qualifiedTextProvider.getClassText(eObject));
584 qualifiedText.append('}');
585
586 return qualifiedText;
587 }
588
589 protected static String getMessageSubstitution(Map<Object, Object> context,
590 Object object) {
591
592 if (object instanceof EObject) {
593 EObject eObject = (EObject) object;
594
595 if (context != null) {
596 EValidator.SubstitutionLabelProvider substitutionLabelProvider = (EValidator.SubstitutionLabelProvider) context
597 .get(EValidator.SubstitutionLabelProvider.class);
598
599 if (substitutionLabelProvider != null) {
600 return substitutionLabelProvider.getObjectLabel(eObject);
601 }
602
603 QualifiedTextProvider qualifiedTestProvider = (QualifiedTextProvider) context
604 .get(QualifiedTextProvider.class);
605
606 if (qualifiedTestProvider != null) {
607 return getQualifiedText(eObject, qualifiedTestProvider);
608 }
609 }
610
611 Resource resource = eObject.eResource();
612
613 if (resource != null) {
614 return resource.getURI().lastSegment() + '#'
615 + resource.getURIFragment(eObject);
616 }
617
618 return EcoreUtil.getIdentification((EObject) object);
619 } else if (object instanceof Collection<?>) {
620 StringBuffer messageSubstitution = new StringBuffer("["); //$NON-NLS-1$
621
622 for (Object element : ((Collection<?>) object)) {
623
624 if (messageSubstitution.length() > 1) {
625 messageSubstitution.append(", "); //$NON-NLS-1$
626 }
627
628 messageSubstitution.append(getMessageSubstitution(context,
629 element));
630 }
631
632 messageSubstitution.append(']');
633
634 return messageSubstitution.toString();
635 } else {
636 return String.valueOf(object);
637 }
638 }
639
640 protected static Object[] getMessageSubstitutions(
641 Map<Object, Object> context, Object object0) {
642 return new Object[]{getMessageSubstitution(context, object0)};
643 }
644
645 protected static Object[] getMessageSubstitutions(
646 Map<Object, Object> context, Object object0, Object object1) {
647 return new Object[]{getMessageSubstitution(context, object0),
648 getMessageSubstitution(context, object1)};
649 }
650
651 protected static Object[] getMessageSubstitutions(
652 Map<Object, Object> context, Object object0, Object object1,
653 Object object2) {
654 return new Object[]{getMessageSubstitution(context, object0),
655 getMessageSubstitution(context, object1),
656 getMessageSubstitution(context, object2)};
657 }
658
659 /**
660 * Safely determines whether <code>object</code> equals
661 * <code>otherObject</code>, i.e. without throwing an exception if
662 * <code>object</code> is <code>null</code>.
663 *
664 * @param object
665 * The first object to compare.
666 * @param otherObject
667 * The second object to compare.
668 * @return <code>true</code> if <code>object</code> equals
669 * <code>otherObject</code>; <code>false</code> otherwise.
670 */
671 public static boolean safeEquals(Object object, Object otherObject) {
672 return object == null
673 ? otherObject == null
674 : object.equals(otherObject);
675 }
676
677 /**
678 * Determines whether the specified string is empty, i.e. is
679 * <code>null</code> or has a length of zero.
680 *
681 * @param string
682 * The string in question.
683 * @return <code>true</code> if the string is empty; <code>false</code>
684 * otherwise.
685 */
686 public static boolean isEmpty(String string) {
687 return string == null || string.length() == 0;
688 }
689
690 /**
691 * Retrieves the first of the specified objects that matches the criteria
692 * used by the specified matcher.
693 *
694 * @param eObjects
695 * The collection of candidate objects.
696 * @param filter
697 * The matcher to be used.
698 * @return The first object that matches the criteria.
699 */
700 public static EObject findEObject(Collection<? extends EObject> eObjects,
701 EObjectMatcher filter) {
702 return findEObject(eObjects.iterator(), filter);
703 }
704
705 /**
706 * Retrieves the first of the specified objects that matches the criteria
707 * used by the specified matcher.
708 *
709 * @param iterator
710 * The iterator for the candidate objects.
711 * @param filter
712 * The matcher to be used.
713 * @return The first object that matches the criteria.
714 */
715 public static EObject findEObject(Iterator<? extends EObject> iterator,
716 EObjectMatcher filter) {
717
718 while (iterator.hasNext()) {
719 EObject eObject = iterator.next();
720
721 if (filter.matches(eObject)) {
722 return eObject;
723 }
724 }
725
726 return null;
727 }
728
729 protected static EClassifier getCommonEType(EClassifier eType,
730 final EClassifier otherEType) {
731
732 if (eType == null || eType.equals(otherEType)) {
733 return eType;
734 } else {
735 return new EcoreSwitch<EClassifier>() {
736
737 @Override
738 public EClassifier caseEClassifier(EClassifier eClassifier) {
739 return EcorePackage.eINSTANCE.getEObject();
740 }
741
742 @Override
743 public EClassifier caseEClass(EClass eClass) {
744
745 if (otherEType instanceof EClass) {
746 EClass otherEClass = (EClass) otherEType;
747
748 if (eClass.isSuperTypeOf(otherEClass)) {
749 return eClass;
750 } else if (otherEClass.isSuperTypeOf(eClass)) {
751 return otherEClass;
752 }
753
754 for (Iterator<EClass> eAllSuperTypes = eClass
755 .getEAllSuperTypes().iterator(); eAllSuperTypes
756 .hasNext();) {
757
758 EClass eSuperType = eAllSuperTypes.next();
759
760 if (eSuperType.isSuperTypeOf(otherEClass)) {
761 return eSuperType;
762 }
763 }
764
765 for (Iterator<EClass> otherEAllSuperTypes = otherEClass
766 .getEAllSuperTypes().iterator(); otherEAllSuperTypes
767 .hasNext();) {
768
769 EClass otherESuperType = otherEAllSuperTypes.next();
770
771 if (otherESuperType.isSuperTypeOf(eClass)) {
772 return otherESuperType;
773 }
774 }
775 }
776
777 return super.caseEClass(eClass);
778 }
779
780 @Override
781 public EClassifier caseEDataType(EDataType eDataType) {
782 return otherEType instanceof EDataType
783 && eDataType.getInstanceClass().equals(
784 ((EDataType) otherEType).getInstanceClass())
785 ? eDataType
786 : EcorePackage.eINSTANCE.getEJavaObject();
787 }
788
789 @Override
790 public EClassifier caseEEnum(EEnum eEnum) {
791 return otherEType instanceof EEnum
792 ? EcorePackage.eINSTANCE.getEEnumerator()
793 : EcorePackage.eINSTANCE.getEJavaObject();
794 }
795 }.doSwitch(eType);
796 }
797 }
798
799 protected static int getLesserLowerBound(int lowerBound, int otherLowerBound) {
800 return Math.min(lowerBound, otherLowerBound);
801 }
802
803 protected static int getGreaterUpperBound(int upperBound,
804 int otherUpperBound) {
805
806 return upperBound == ETypedElement.UNBOUNDED_MULTIPLICITY
807 || otherUpperBound == ETypedElement.UNBOUNDED_MULTIPLICITY
808 ? ETypedElement.UNBOUNDED_MULTIPLICITY
809 : Math.max(upperBound, otherUpperBound);
810 }
811
812 /**
813 * Obtains a valid Java identifier based on the specified name.
814 *
815 * @param name
816 * The name from which to obtain a valid identifier.
817 * @return A valid (Java) identifier.
818 */
819 public static String getValidJavaIdentifier(String name) {
820 return getValidJavaIdentifier(name, new StringBuffer()).toString();
821 }
822
823 /**
824 * Appends a valid Java identifier based on the specified name to the
825 * specified buffer.
826 *
827 * @param name
828 * The name from which to obtain the valid identifier.
829 * @param validJavaIdentifier
830 * The buffer to which to append the valid identifier.
831 * @return The buffer.
832 */
833 protected static StringBuffer getValidJavaIdentifier(String name,
834 StringBuffer validJavaIdentifier) {
835
836 if (isEmpty(name)) {
837 validJavaIdentifier.append('_');
838 } else {
839 char char_0 = name.charAt(0);
840
841 if (Character.isJavaIdentifierStart(char_0)) {
842 validJavaIdentifier.append(char_0);
843 } else {
844 validJavaIdentifier.append('_');
845
846 if (Character.isJavaIdentifierPart(char_0)) {
847 validJavaIdentifier.append(char_0);
848 }
849 }
850
851 for (int i = 1; i < name.length(); i++) {
852 char char_i = name.charAt(i);
853
854 if (Character.isJavaIdentifierPart(char_i)) {
855 validJavaIdentifier.append(char_i);
856 }
857 }
858 }
859
860 return validJavaIdentifier;
861 }
862
863 protected static boolean isNCNameStart(char c) {
864 return XMLTypeUtil.isNCNameStart(c);
865 }
866
867 protected static boolean isNCNamePart(char c) {
868 return XMLTypeUtil.isNCNamePart(c);
869 }
870
871 protected static String getValidNCName(String name) {
872 return getValidNCName(name, new StringBuffer()).toString();
873 }
874
875 protected static StringBuffer getValidNCName(String name,
876 StringBuffer validNCName) {
877
878 if (isEmpty(name)) {
879 validNCName.insert(0, '_');
880 } else {
881
882 for (int i = name.length(); --i > 0;) {
883 char char_i = name.charAt(i);
884
885 if (isNCNamePart(char_i)) {
886 validNCName.insert(0, char_i);
887 }
888 }
889
890 char char_0 = name.charAt(0);
891
892 if (isNCNameStart(char_0)) {
893 validNCName.insert(0, char_0);
894 } else {
895
896 if (isNCNamePart(char_0)) {
897 validNCName.insert(0, char_0);
898 }
899
900 validNCName.insert(0, '_');
901 }
902 }
903
904 return validNCName;
905 }
906
907 /**
908 * Obtains a valid XMI identifier for the specified object based on the URI
909 * fragment segments of its containment hierarchy.
910 *
911 * @param internalEObject
912 * The object for which to obtain an XMI identifier.
913 * @return An XMI identifier for the object.
914 */
915 public static String getXMIIdentifier(InternalEObject internalEObject) {
916 return getXMIIdentifier(internalEObject, new StringBuffer()).toString();
917 }
918
919 protected static StringBuffer getXMIIdentifier(
920 InternalEObject internalEObject, StringBuffer xmiIdentifier) {
921 InternalEObject eInternalContainer = internalEObject
922 .eInternalContainer();
923 Resource.Internal eDirectResource = internalEObject.eDirectResource();
924
925 while (eInternalContainer != null && eDirectResource == null) {
926 getValidNCName(eInternalContainer.eURIFragmentSegment(
927 internalEObject.eContainingFeature(), internalEObject),
928 xmiIdentifier);
929
930 internalEObject = eInternalContainer;
931 eInternalContainer = internalEObject.eInternalContainer();
932 eDirectResource = internalEObject.eDirectResource();
933
934 if (eInternalContainer != null && eDirectResource == null) {
935 xmiIdentifier.insert(0, '-');
936 }
937 }
938
939 if (eDirectResource != null) {
940 int index = eDirectResource.getContents().indexOf(internalEObject);
941 int length = xmiIdentifier.length();
942
943 if (index > 0 || length == 0) {
944
945 if (length > 0) {
946 xmiIdentifier.insert(0, '-');
947 }
948
949 xmiIdentifier.insert(0, index);
950 xmiIdentifier.insert(0, '_');
951 }
952 }
953
954 return xmiIdentifier;
955 }
956
957 /**
958 * Counts the number of instances of the specified classifier.
959 *
960 * @param iterator
961 * The iterator for the candidate objects.
962 * @param eClassifier
963 * The classifier in question.
964 * @return The number of the instances of the classifier.
965 */
966 public static int getInstanceCount(Iterator<?> iterator,
967 EClassifier eClassifier) {
968 int count = 0;
969
970 while (iterator.hasNext()) {
971
972 if (eClassifier.isInstance(iterator.next())) {
973 count++;
974 }
975 }
976
977 return count;
978 }
979
980 protected static boolean addConstraint(EModelElement eModelElement,
981 String constraint) {
982
983 if (isEmpty(constraint)) {
984 return false;
985 } else {
986 List<String> constraints = new ArrayList<String>(EcoreUtil
987 .getConstraints(eModelElement));
988
989 boolean result = constraints.add(constraint);
990
991 EcoreUtil.setConstraints(eModelElement, constraints);
992
993 return result;
994 }
995 }
996
997 protected static void addDocumentation(EModelElement eModelElement,
998 String text) {
999
1000 if (!isEmpty(text)) {
1001 String documentation = EcoreUtil.getDocumentation(eModelElement);
1002
1003 EcoreUtil.setDocumentation(eModelElement, documentation == null
1004 ? text
1005 : documentation + LINE_SEPARATOR + text);
1006 }
1007 }
1008
1009 protected static Collection<EObject> getRootContainers(
1010 Collection<? extends EObject> eObjects) {
1011 Collection<EObject> rootContainers = new UniqueEList.FastCompare<EObject>();
1012
1013 for (Iterator<? extends EObject> i = eObjects.iterator(); i.hasNext();) {
1014 rootContainers.add(EcoreUtil.getRootContainer(i.next()));
1015 }
1016
1017 return rootContainers;
1018 }
1019
1020 /**
1021 * Retrieves an iterator over the content tree of the specified object which
1022 * optionally includes the root object and/or copies contents while
1023 * iterating.
1024 *
1025 * @param eObject
1026 * The root of the content hierarchy.
1027 * @param includeRoot
1028 * Whether to include the root object.
1029 * @param defensiveCopy
1030 * Whether to copy contents while iterating.
1031 * @return A content tree iterator.
1032 */
1033 public static <T> TreeIterator<T> getAllContents(EObject eObject,
1034 boolean includeRoot, final boolean defensiveCopy) {
1035 return new AbstractTreeIterator<T>(eObject, includeRoot) {
1036
1037 private static final long serialVersionUID = 1L;
1038
1039 @SuppressWarnings("unchecked")
1040 @Override
1041 protected Iterator<T> getChildren(Object object) {
1042 return (Iterator<T>) (defensiveCopy
1043 ? new ArrayList<EObject>(((EObject) object).eContents())
1044 .iterator()
1045 : ((EObject) object).eContents().iterator());
1046 }
1047 };
1048 }
1049
1050 /**
1051 * Creates an annotation with the specified source on the specified model
1052 * element.
1053 *
1054 * @param eModelElement
1055 * The model element on which to create the annotation.
1056 * @param source
1057 * The source for the new annotation.
1058 * @return A new annotation.
1059 */
1060 public static EAnnotation createEAnnotation(EModelElement eModelElement,
1061 String source) {
1062 EAnnotation eAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
1063 eAnnotation.setEModelElement(eModelElement);
1064 eAnnotation.setSource(source);
1065 return eAnnotation;
1066 }
1067
1068 /**
1069 * Retrieves the annotation with the specified source on the specified model
1070 * element, optionally creating one on demand if no such annotation exists.
1071 *
1072 * @param eModelElement
1073 * The model element from/on which to retrieve/create the
1074 * annotation.
1075 * @param source
1076 * The source for the (new) annotation.
1077 * @param createOnDemand
1078 * Whether to create one if no such annotation exists.
1079 * @return The (new) annotation.
1080 */
1081 public static EAnnotation getEAnnotation(EModelElement eModelElement,
1082 String source, boolean createOnDemand) {
1083 EAnnotation eAnnotation = eModelElement.getEAnnotation(source);
1084
1085 return eAnnotation == null && createOnDemand
1086 ? createEAnnotation(eModelElement, source)
1087 : eAnnotation;
1088 }
1089
1090 /**
1091 * Retrieves a collection of non-navigable inverse references to the
1092 * specified object.
1093 *
1094 * @param eObject
1095 * The referenced object.
1096 * @return The non-navigable inverse references to the object.
1097 */
1098 public static Collection<EStructuralFeature.Setting> getNonNavigableInverseReferences(
1099 EObject eObject) {
1100 ECrossReferenceAdapter crossReferenceAdapter = ECrossReferenceAdapter
1101 .getCrossReferenceAdapter(eObject);
1102 return crossReferenceAdapter == null
1103 ? Collections.<EStructuralFeature.Setting> emptyList()
1104 : crossReferenceAdapter.getNonNavigableInverseReferences(eObject);
1105 }
1106
1107 /**
1108 * Retrieves a collection of inverse references to the specified object.
1109 *
1110 * @param eObject
1111 * The referenced object.
1112 * @return The inverse references to the object.
1113 */
1114 public static Collection<EStructuralFeature.Setting> getInverseReferences(
1115 EObject eObject) {
1116 ECrossReferenceAdapter crossReferenceAdapter = ECrossReferenceAdapter
1117 .getCrossReferenceAdapter(eObject);
1118 return crossReferenceAdapter == null
1119 ? Collections.<EStructuralFeature.Setting> emptyList()
1120 : crossReferenceAdapter.getInverseReferences(eObject);
1121 }
1122
1123 protected static void removeReferences(EObject eObject,
1124 EObject ancestorEObject) {
1125
1126 for (EStructuralFeature.Setting nonNavigableInverseReference : new ArrayList<EStructuralFeature.Setting>(
1127 getNonNavigableInverseReferences(eObject))) {
1128
1129 if (nonNavigableInverseReference.getEStructuralFeature()
1130 .isChangeable()
1131 && (ancestorEObject == null || !EcoreUtil.isAncestor(
1132 ancestorEObject, nonNavigableInverseReference.getEObject()))) {
1133
1134 EcoreUtil.remove(nonNavigableInverseReference, eObject);
1135 }
1136 }
1137
1138 for (EReference eReference : eObject.eClass().getEAllReferences()) {
1139
1140 if (eReference.isChangeable() && !eReference.isContainer()
1141 && !eReference.isContainment() && eObject.eIsSet(eReference)) {
1142
1143 if (eReference.isMany()) {
1144
1145 for (Iterator<?> values = ((List<?>) eObject
1146 .eGet(eReference)).iterator(); values.hasNext();) {
1147
1148 Object value = values.next();
1149
1150 if (ancestorEObject == null
1151 || !EcoreUtil.isAncestor(ancestorEObject,
1152 (EObject) value)) {
1153
1154 values.remove();
1155 }
1156 }
1157 } else {
1158
1159 if (ancestorEObject == null
1160 || !EcoreUtil.isAncestor(ancestorEObject,
1161 (EObject) eObject.eGet(eReference))) {
1162
1163 eObject.eUnset(eReference);
1164 }
1165 }
1166 }
1167 }
1168 }
1169
1170 protected static void destroy(EObject eObject) {
1171
1172 if (eObject.eContents().isEmpty()) {
1173 removeReferences(eObject, eObject);
1174 eObject.eAdapters().clear();
1175 } else {
1176
1177 for (Iterator<EObject> allContents = getAllContents(eObject, true,
1178 false); allContents.hasNext();) {
1179
1180 removeReferences(allContents.next(), eObject);
1181 }
1182
1183 for (Iterator<EObject> allContents = getAllContents(eObject, true,
1184 false); allContents.hasNext();) {
1185
1186 (allContents.next()).eAdapters().clear();
1187 }
1188 }
1189
1190 EcoreUtil.remove(eObject);
1191 }
1192
1193 protected static void destroyAll(Collection<? extends EObject> eObjects) {
1194
1195 for (Iterator<? extends EObject> o = eObjects.iterator(); o.hasNext();) {
1196 destroy(o.next());
1197 }
1198 }
1199
1200 /**
1201 * Loads a resource with the specified URI into the specified resource set
1202 * and retrieves the first instance of the specified class from its
1203 * contents.
1204 *
1205 * @param resourceSet
1206 * The resource set into which to load the resource.
1207 * @param uri
1208 * The URI of the resource to be loaded.
1209 * @param eClass
1210 * The class of the object to be retrieved.
1211 * @return The first instance of the class in the resource.
1212 */
1213 public static <T> T load(ResourceSet resourceSet, URI uri, EClass eClass) {
1214
1215 try {
1216 @SuppressWarnings("unchecked")
1217 T objectByType = (T) EcoreUtil.getObjectByType(resourceSet
1218 .getResource(uri, true).getContents(), eClass);
1219 return objectByType;
1220 } catch (Exception e) {
1221 return null;
1222 }
1223 }
1224
1225 protected static boolean intersect(Collection<?> collection,
1226 Collection<?> otherCollection) {
1227 return !Collections.disjoint(collection, otherCollection);
1228 }
1229
1230 /**
1231 * Load a class from the specified system property.
1232 * Any exceptions resulting from class loading failures will be logged.
1233 *
1234 * If the system property is separated with a ':' then the first part indicates
1235 * the pluginId and the trailing part indicates the class name.
1236 *
1237 * @param systemProperty
1238 * The system property containing the location of the class to be loaded.
1239 * @return
1240 * The loaded class.
1241 * @since 1.5
1242 */
1243 @SuppressWarnings("unchecked")
1244 public static <T> T loadClassFromSystemProperty(String systemProperty) {
1245 String property = System.getProperty(systemProperty);
1246
1247 if (!isEmpty(property)) {
1248
1249 try {
1250 int index = property.indexOf(':');
1251
1252 if (index != -1) {
1253 return (T) org.eclipse.emf.common.CommonPlugin.loadClass(
1254 property.substring(0, index),
1255 property.substring(index + 1)).newInstance();
1256 } else {
1257 return (T) Class.forName(property).newInstance();
1258 }
1259 } catch (Exception e) {
1260 CommonPlugin.INSTANCE.log(e);
1261 }
1262 }
1263
1264 return null;
1265 }
1266 }