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    }