diff options
Diffstat (limited to 'plugins/uml')
9 files changed, 940 insertions, 7 deletions
diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/META-INF/MANIFEST.MF b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/META-INF/MANIFEST.MF index 82a6f36772f..7b725191468 100644 --- a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/META-INF/MANIFEST.MF +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/META-INF/MANIFEST.MF @@ -3,6 +3,7 @@ Export-Package: org.eclipse.papyrus.uml.modelrepair, org.eclipse.papyrus.uml.modelrepair.handler, org.eclipse.papyrus.uml.modelrepair.internal.participants;x-internal:=true, org.eclipse.papyrus.uml.modelrepair.internal.stereotypes;x-friends:="org.eclipse.papyrus.migration.rsa", + org.eclipse.papyrus.uml.modelrepair.internal.uripattern, org.eclipse.papyrus.uml.modelrepair.internal.validation;x-internal:=true, org.eclipse.papyrus.uml.modelrepair.service, org.eclipse.papyrus.uml.modelrepair.ui, @@ -15,7 +16,7 @@ Require-Bundle: org.eclipse.papyrus.uml.extensionpoints;bundle-version="[1.2.0,2 org.eclipse.uml2.uml.edit;bundle-version="[5.1.0,6.0.0)" Bundle-Vendor: Eclipse Modeling Project Bundle-ActivationPolicy: lazy -Bundle-Version: 1.2.0.qualifier +Bundle-Version: 1.2.1.qualifier Bundle-Name: Model Repair Bundle-Activator: org.eclipse.papyrus.uml.modelrepair.Activator Bundle-ManifestVersion: 2 diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/plugin.xml b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/plugin.xml index aa3de8549fe..139bc9c5224 100644 --- a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/plugin.xml +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/plugin.xml @@ -2,6 +2,7 @@ <?eclipse version="3.4"?>
<plugin>
<extension-point id="org.eclipse.papyrus.uml.modelrepair.profileSwitchPreconditions" name="Profile Switch Preconditions" schema="schema/profileSwitchPreconditions.exsd"/>
+ <extension-point id="org.eclipse.papyrus.uml.modelrepair.profileNamespaceURIPattern" name="Profile Namespace URI Pattern" schema="schema/profileNamespaceURIPattern.exsd"/>
<extension
point="org.eclipse.ui.menus">
<menuContribution
diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/pom.xml b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/pom.xml index c5fe2b7f076..a844c7378ce 100644 --- a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/pom.xml +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/pom.xml @@ -7,6 +7,6 @@ <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>org.eclipse.papyrus.uml.modelrepair</artifactId> - <version>1.2.0-SNAPSHOT</version> + <version>1.2.1-SNAPSHOT</version> <packaging>eclipse-plugin</packaging> </project> diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/schema/profileNamespaceURIPattern.exsd b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/schema/profileNamespaceURIPattern.exsd new file mode 100644 index 00000000000..031ed27ce8b --- /dev/null +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/schema/profileNamespaceURIPattern.exsd @@ -0,0 +1,173 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.papyrus.uml.modelrepair" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appinfo> + <meta.schema plugin="org.eclipse.papyrus.uml.modelrepair" id="profileNamespaceURIPattern" name="Profile Namespace URI Pattern"/> + </appinfo> + <documentation> + This extension point is for supporting the profile migration mechanism in Papyrus. +It provides a regex pattern that identifies the namespace URIs of a profile and enables to extract version information from the URI through pattern groups. +During the migration, only the namespace URIs minus the version information is used to identify profiles. +Even if no version information is encoded in the URI, profile providers may use this extension point to enable the automatic migration of their profiles within Papyrus. + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appinfo> + <meta.element /> + </appinfo> + </annotation> + <complexType> + <sequence minOccurs="1" maxOccurs="unbounded"> + <element ref="profileNamespaceURIPattern"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appinfo> + <meta.attribute translatable="true"/> + </appinfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="profileNamespaceURIPattern"> + <complexType> + <attribute name="uriPattern" type="string" use="required"> + <annotation> + <documentation> + Regex pattern identifying profile namespace URIs during the profile migration. Groups in the pattern are used to extract versioning information. +Backslashes ('\') should not be escaped. + </documentation> + </annotation> + </attribute> + <attribute name="versionFormat" type="string"> + <annotation> + <documentation> + MessageFormat string to produce the versioning string from the groups extracted by the uriPattern. Indices in the format correspond to the indices of the groups matched by the pattern, e.g., {0} refers to group zero holding the entire matched namespace URI and {1} refers to the first matched group. If no format is provided, a comma-separated version string is produced. + </documentation> + </annotation> + </attribute> + </complexType> + </element> + + <annotation> + <appinfo> + <meta.section type="since"/> + </appinfo> + <documentation> + 1.2.1 + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="examples"/> + </appinfo> + <documentation> + <p><strong>Namespace URI Matching</strong></p> +<pre class="Example"> +<span class="code SchemaTag">&lt;extension point=</span><span class="code SchemaCstring">"org.eclipse.papyrus.uml.modelrepair.profileNamespaceURIPattern"</span><span class="code SchemaTag">&gt;</span> + <span class="code SchemaTag">&lt;profileNamespaceURIPattern</span> <span class="code SchemaTag">uriPattern=</span><span class="code SchemaCstring">"^http://www\.eclipse\.org/my/profile/version/([^/]+)/Language/([^/]+)/.*$"</span> <span class="code SchemaTag">/&gt;</span> +<span class="code SchemaTag">&lt;/extension&gt;</span> +</pre> + +<p>Input URI A: http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageA</p> +<ul> + <li>Matched Namespace URI: "http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageA"</li> + <li>Versionless Namespace URI: "http://www.eclipse.org/my/profile/version//Language//PackageA"</li> + <li>Version: "7,7.0.1" (default format)</li> +</ul> + +<p>Input URI B: http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageB</p> +<ul> + <li>Matched Namespace URI: "http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageA"</li> + <li>Versionless Namespace URI: "http://www.eclipse.org/my/profile/version//Language//PackageB"</li> + <li>Version: "7,7.0.1" (default format)</li> +</ul> + +<p>Input URI C: http://www.eclipse.org/my/profile/version/6/Language/6.0/PackageB</p> +<ul> + <li>Matched Namespace URI: "http://www.eclipse.org/my/profile/version/6/Language/6.0/PackageB"</li> + <li>Versionless Namespace URI: "http://www.eclipse.org/my/profile/version//Language//PackageB"</li> + <li>Version: "6,6.0" (default format)</li> +</ul> + +<p>During the migration, input B and C are treated as the same profile, just with a different version, whereas input A is considered as a different profile.</p> + +<p><strong>Version Formatting</strong></p> +<pre class="Example"> +<span class="code SchemaTag">&lt;profileNamespaceURIPattern ... </span><span class="code SchemaTag">versionFormat=</span><span class="code SchemaCstring">"-{1}-"</span><span class="code SchemaTag">/&gt;</span> +</pre> + +<p>Input URI A: http://www.eclipse.org/my/profile/version/7/Language/PackageA</p> +<ul> + <li>Version: "-7-"</li> +</ul> + +<pre class="Example"> +<span class="code SchemaTag">&lt;profileNamespaceURIPattern ... </span><span class="code SchemaTag">versionFormat=</span><span class="code SchemaCstring">"{1} - {2}"</span><span class="code SchemaTag">/&gt;</span> +</pre> + +<p>Input URI A: http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageA</p> +<ul> + <li>Version: "7 - 7.0.1"</li> +</ul> + +<pre class="Example"> +<span class="code SchemaTag">&lt;profileNamespaceURIPattern ... </span><span class="code SchemaTag">versionFormat=</span><span class="code SchemaCstring">"{0} | {1}"</span><span class="code SchemaTag">/&gt;</span> +</pre> + +<p>Input URI A: http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageA</p> +<ul> + <li>Version: "http://www.eclipse.org/my/profile/version/7/Language/7.0.1/PackageA | 7"</li> +</ul> + +<p>Special index zero conforms to group zero of the pattern match, which is the entire matched namespace URI.</p> + </documentation> + </annotation> + + <annotation> + <appinfo> + <meta.section type="apiinfo"/> + </appinfo> + <documentation> + An internal registry (ProfileNamespaceURIPatternRegistry) is used to control the registering and unregistering of the profile namespace URI patterns. +This registry is initialized with all extension points, but a programatical removal of provided patterns is possible. + </documentation> + </annotation> + + + <annotation> + <appinfo> + <meta.section type="copyright"/> + </appinfo> + <documentation> + Copyright (c) 2016 EclipseSource and others. +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +which accompanies this distribution, and is available at +http://www.eclipse.org/legal/epl-v10.html + </documentation> + </annotation> + +</schema> diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java index 1cc14a00c2c..fd764104127 100644 --- a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/stereotypes/ZombieStereotypesDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015 CEA, Christian W. Damus, and others. + * Copyright (c) 2014, 2016 CEA, Christian W. Damus, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -9,8 +9,9 @@ * Contributors: * Christian W. Damus (CEA) - Initial API and implementation * Christian W. Damus - bug 399859 - * Christian W. Damus - bug 451338 + * Christian W. Damus - bug 451338 * Christian W. Damus - bug 436666 + * Martin Fleck - bug 496307 * */ package org.eclipse.papyrus.uml.modelrepair.internal.stereotypes; @@ -41,6 +42,8 @@ import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.papyrus.infra.core.utils.AdapterUtils; import org.eclipse.papyrus.infra.services.labelprovider.service.LabelProviderService; import org.eclipse.papyrus.uml.modelrepair.internal.participants.StereotypesUtil; +import org.eclipse.papyrus.uml.modelrepair.internal.uripattern.ProfileNamespaceURIPatternComparison; +import org.eclipse.papyrus.uml.modelrepair.internal.uripattern.ProfileNamespaceURIPatternRegistry; import org.eclipse.papyrus.uml.tools.helper.IProfileApplicationDelegate; import org.eclipse.papyrus.uml.tools.helper.ProfileApplicationDelegateRegistry; import org.eclipse.uml2.uml.Element; @@ -53,6 +56,7 @@ import org.eclipse.uml2.uml.util.UMLUtil; import com.google.common.base.Function; import com.google.common.base.Objects; +import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -178,7 +182,7 @@ public class ZombieStereotypesDescriptor { * <li>adapts to {@link EPackage} to provide the EMF schema that is unresolved or otherwise broken</li> * <li>may possibly adapt to {@link IStereotypeOrphanGroup} representing stereotype applications from a broken schema that are not repairable by profile migration and so are treated separately with a distinct set of available actions * </ul> - * + * * @return the zombie schemas that are detected */ public Collection<? extends IAdaptable> getZombieSchemas() { @@ -308,7 +312,7 @@ public class ZombieStereotypesDescriptor { /** * Obtains a repair action of the specified {@code kind} for a broken {@code schema}, if it is available. - * + * * @param schema * a schema to repair * @param kind @@ -454,9 +458,20 @@ public class ZombieStereotypesDescriptor { result = schema1 == schema2; if (!result && (schema1 != null)) { // Implies that schema2 != null, also - result = Objects.equal(schema1.getNsURI(), schema2.getNsURI()); + String schema1uri = schema1.getNsURI(); + String schema2uri = schema2.getNsURI(); + result = Objects.equal(schema1uri, schema2uri); if (!result) { + // No perfect equality, try to use provided profile package patterns + + // Try to find find a pattern comparison that matches both URIs + Optional<ProfileNamespaceURIPatternComparison> comparison = ProfileNamespaceURIPatternRegistry.INSTANCE.tryFindComparison(schema1uri, schema2uri); + if (comparison.isPresent()) { + return comparison.get().isEqualVersionlessNamespaceURI(); + } + + // No pattern found that handles the URIs // Maybe one is a proxy whose URI is the schema-location of the other (being a demand-created package) URI uri1 = guessURI(schema1); URI uri2 = guessURI(schema2); @@ -479,6 +494,7 @@ public class ZombieStereotypesDescriptor { } return result; + } static URI guessURI(EPackage schema) { diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPattern.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPattern.java new file mode 100644 index 00000000000..4805e64d123 --- /dev/null +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPattern.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2016 EclipseSource Services GmbH and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.papyrus.uml.modelrepair.internal.uripattern; + +import java.text.MessageFormat; +import java.util.regex.Pattern; + +/** + * A profile namespace URI pattern consists of a Regex pattern and an optional version format. + * The {@link Pattern} can split profile namespace URIs into versionless namespace URIs which can be used as identification and and the version part. + * The version format allows for individual formatting of the extracted version information and needs to conform to the {@link MessageFormat}. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +public class ProfileNamespaceURIPattern { + + /** Regex pattern to split the URI into the profile-identifying part and the versioning part. */ + private final Pattern pattern; + + /** Message format to format the version parts extracted from the namespace URI */ + private final MessageFormat versionFormat; + + /** + * Creates a new profile namespace URI pattern with the given namespace URI pattern. + * The URI pattern is a Regex that can split namespace URIs into the profile-identifying part (without version) and the versioning part. + * This constructor uses the default versioning formatting producing a comma-separated version string. + * + * @param pattern + * pattern used to split the namespace URI + */ + public ProfileNamespaceURIPattern(final String pattern) { + this(pattern, null); + } + + /** + * Creates a new profile namespace URI pattern with the given namespace URI pattern and the provided version format. + * The URI pattern is a Regex that can split namespace URIs into the profile-identifying part (without version) and the versioning part. + * The version format allows for individual formatting of the extracted version information and needs to conform to the {@link MessageFormat}. + * Indices in the format correspond to the indices of the groups matched by the pattern, e.g., {0} refers to group zero holding the entire pattern and {1} refers to the first matched group. + * + * @param pattern + * Regex pattern + * @param versionFormat + * format for the versioning part of the matched namespace URI + * @see Pattern + * @see MessageFormat + */ + public ProfileNamespaceURIPattern(final String pattern, final String versionFormat) { + this.pattern = Pattern.compile(pattern); + if (versionFormat != null) { + this.versionFormat = new MessageFormat(versionFormat); + } else { + this.versionFormat = null; + } + } + + /** + * Returns the Regex pattern that can split namespace URIs into the profile-identifying part and the versioning part. + * + * @return the Regex pattern + */ + public Pattern getRegexPattern() { + return pattern; + } + + /** + * Returns the format that is used to create the {@link ProfileNamespaceURIPatternMatchResult#getVersion() version} string from the groups + * of the {@link #getRegexPattern() namespace URI pattern}. If no such format was given, this method returns null. + * + * @return the versionFormat or null if no such format was given + */ + public MessageFormat getVersionFormat() { + return versionFormat; + } + + + /** + * Compares the two given URIs based on how they {@link ProfileNamespaceURIPatternMatchResult match} this pattern. + * Only if both URIs match the pattern, a {@link ProfileNamespaceURIPatternComparison#isValid() valid} comparison result is returned. + * + * @param lhsUri + * left-hand side URI for the comparison + * @param rhsUri + * right-hand side URI for the comparison + * @return a non-null comparison result, which may or may not be valid + */ + public ProfileNamespaceURIPatternComparison compare(final String lhsUri, final String rhsUri) { + final ProfileNamespaceURIPatternMatchResult lhsPattern = match(lhsUri); + if (lhsPattern.hasMatched()) { + final ProfileNamespaceURIPatternMatchResult rhsPattern = match(rhsUri); + if (rhsPattern.hasMatched()) { + // both uris match + return new ProfileNamespaceURIPatternComparison(lhsPattern, rhsPattern); + } + } + // none or only one uri matches + return ProfileNamespaceURIPatternComparison.INVALID; + } + + /** + * Matches the given uri with this namespace URI pattern. + * + * @param uri + * uri to match + * @return a non-null match result, which may or may not {@link ProfileNamespaceURIPatternMatchResult#hasMatched() have matched}. + */ + public ProfileNamespaceURIPatternMatchResult match(final String uri) { + if (uri == null) { + return ProfileNamespaceURIPatternMatchResult.NO_MATCH; + } + return new ProfileNamespaceURIPatternMatchResult(pattern.matcher(uri), versionFormat); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((pattern == null) ? 0 : pattern.pattern().hashCode()); + result = prime * result + ((versionFormat == null) ? 0 : versionFormat.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ProfileNamespaceURIPattern other = (ProfileNamespaceURIPattern) obj; + if (pattern == null) { + if (other.pattern != null) { + return false; + } + } else if (!pattern.pattern().equals(other.pattern.pattern())) { + return false; + } + if (versionFormat == null) { + if (other.versionFormat != null) { + return false; + } + } else if (!versionFormat.equals(other.versionFormat)) { + return false; + } + return true; + } +} + diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternComparison.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternComparison.java new file mode 100644 index 00000000000..b7531a30553 --- /dev/null +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternComparison.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2016 EclipseSource Services GmbH and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.papyrus.uml.modelrepair.internal.uripattern; + +/** + * This convenience class providing comparison methods for two {@link ProfileNamespaceURIPatternMatchResult}es. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +public class ProfileNamespaceURIPatternComparison { + + /** Constant representing an invalid comparison */ + public static final ProfileNamespaceURIPatternComparison INVALID = new ProfileNamespaceURIPatternComparison( + ProfileNamespaceURIPatternMatchResult.NO_MATCH, ProfileNamespaceURIPatternMatchResult.NO_MATCH); + + /** Left-hand side pattern match of the comparison */ + private final ProfileNamespaceURIPatternMatchResult lhsPatternMatch; + + /** Right-hand side pattern match of the comparison */ + private final ProfileNamespaceURIPatternMatchResult rhsPatternMatch; + + /** + * Creates a new comparison based on the given two matches. + * The resulting comparison is only valid if both matches have actually matched. + * All comparisons on an invalid comparison yield false as return value. + * + * @param lhsPatternMatch + * first match + * @param rhsPatternMatch + * second match + * @see ProfileNamespaceURIPattern + * @see ProfileNamespaceURIPatternRegistry + */ + public ProfileNamespaceURIPatternComparison(final ProfileNamespaceURIPatternMatchResult lhsPatternMatch, + final ProfileNamespaceURIPatternMatchResult rhsPatternMatch) { + this.lhsPatternMatch = lhsPatternMatch; + this.rhsPatternMatch = rhsPatternMatch; + } + + /** + * Left-hand side pattern match given when constructing this comparison. + * + * @return left-hand side pattern match + */ + public ProfileNamespaceURIPatternMatchResult getLHSPatternMatch() { + return lhsPatternMatch; + } + + /** + * Right-hand side pattern match given when constructing this comparison. + * + * @return second pattern match + */ + public ProfileNamespaceURIPatternMatchResult getRHSPatternMatch() { + return rhsPatternMatch; + } + + /** + * This comparison is valid if both pattern matches have matched. + * + * @return true if both pattern matches are valid, false otherwise + */ + public boolean isValid() { + return getLHSPatternMatch() != null && getRHSPatternMatch() != null + && getLHSPatternMatch().hasMatched() && getRHSPatternMatch().hasMatched(); + } + + /** + * Returns true if both pattern matches have the same {@link ProfileNamespaceURIPatternMatchResult#getVersion() version string}. + * If this comparison is invalid, then this method returns false. + * + * @return true if both pattern matches refer to the same version, false otherwise or if this comparison is invalid + */ + public boolean isEqualVersion() { + return isValid() + && getLHSPatternMatch().getVersion().equals(getRHSPatternMatch().getVersion()); + } + + /** + * Returns true if both pattern matches have the same {@link ProfileNamespaceURIPatternMatchResult#getVersionlessNamespaceURI() versionless namespace URI}. + * If this comparison is invalid, then this method returns false. + * + * @return true if both pattern matches refer to the same versionless namespace URI, false otherwise or if this comparison is invalid + */ + public boolean isEqualVersionlessNamespaceURI() { + return isValid() + && getLHSPatternMatch().getVersionlessNamespaceURI().equals(getRHSPatternMatch().getVersionlessNamespaceURI()); + } + + /** + * Returns true if both pattern matches have the same {@link ProfileNamespaceURIPatternMatchResult#getNamespaceURI() namespace URI}. + * If this comparison is invalid, then this method returns false. + * + * @return true if both pattern matches refer to the same namespace URI, false otherwise or if this comparison is invalid + */ + public boolean isEqualNamespaceURI() { + return isValid() + && getLHSPatternMatch().getNamespaceURI().equals(getRHSPatternMatch().getNamespaceURI()); + } + + @Override + public String toString() { + if (!isValid()) { + return "Invalid comparison."; + } + return getLHSPatternMatch() + " versus " + getRHSPatternMatch(); + } +} diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternMatchResult.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternMatchResult.java new file mode 100644 index 00000000000..7238931d98b --- /dev/null +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternMatchResult.java @@ -0,0 +1,231 @@ +/******************************************************************************* + * Copyright (c) 2016 EclipseSource Services GmbH and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.papyrus.uml.modelrepair.internal.uripattern; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class evaluates a {@link Pattern#matches(String, CharSequence) pattern match} with regards to a {@link ProfileNamespaceURIPattern} + * and provides convenience methods to access the profile-identifying part (without version) and versioning part of the matched URI. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +public class ProfileNamespaceURIPatternMatchResult { + + /** Constant representing an unsuccessful match */ + public static final ProfileNamespaceURIPatternMatchResult NO_MATCH = new ProfileNamespaceURIPatternMatchResult(null, null); + + /** Result from the matching process, null if the matching was not successful */ + private MatchResult regexMatchResult; + + /** Format used to format the versioning part of the matched URI */ + private MessageFormat versionFormat; + + /** Cached strings for all group parts in the match */ + private String[] groups; + + /** Cached strings for all non-group parts in the match */ + private String[] nonGroups; + + /** The entire matched namespace URI */ + private String namespaceURI; + + /** The profile-identifying, versionless part of the match, i.e., a concatenation of the non-group parts in the match */ + private String versionlessNamespaceURI; + + /** The formatted versioning string of the match based on the given message format and the matched groups */ + private String version; + + /** + * Creates a new match result based on the given matcher. + * If the matcher does not match the {@link Matcher#matches() entire string}, then this match result is considered {@link #hasMatched() unsuccessful} + * and all methods querying the result will return null. + * + * @param matcher + * match engine + * @see ProfileNamespaceURIPattern + * @see ProfileNamespaceURIPatternRegistry + */ + public ProfileNamespaceURIPatternMatchResult(final Matcher matcher) { + this(matcher, null); + } + + /** + * Creates a new match based on the given matcher and the provided format for the versioning part of the match. + * If the matcher does not match the {@link Matcher#matches() entire string}, then this match result is considered {@link #hasMatched() unsuccessful} + * and all methods querying the result will return null. + * The version format allows for individual formatting of the extracted version information and needs to conform to the {@link MessageFormat}. + * Indices in the format correspond to the indices of the groups matched by the pattern, e.g., {0} refers to group zero holding the entire pattern and {1} refers to the first matched group. + * The version format will only be used if the match is successful and is used to produce the {@link #getVersion() version string} of the matched URI. + * + * @param matcher + * match engine + * @param versionFormat + * format for the versioning part of the matched namespace URI + * @see ProfileNamespaceURIPattern + * @see ProfileNamespaceURIPatternRegistry + */ + public ProfileNamespaceURIPatternMatchResult(final Matcher matcher, final MessageFormat versionFormat) { + if (matcher != null && matcher.matches()) { + this.regexMatchResult = matcher.toMatchResult(); + this.versionFormat = versionFormat; + } + } + + /** + * Returns the format used to produce the {@link #getVersion() version string} from all version parts extracted from the matched URI. + * Indices in the format correspond to the indices of the groups matched by the pattern, e.g., {0} refers to group zero holding the entire pattern and {1} refers to the first matched group. + * + * @return the versionFormat + */ + public MessageFormat getVersionFormat() { + return versionFormat; + } + + /** + * Returns true if a successful match could be produced for the given URI considering the underlying pattern, false otherwise. + * If no successful match could be produced, all methods querying the result will return null. + * + * @return true if the match was successful, false otherwise. + */ + public boolean hasMatched() { + return getRegexMatchResult() != null; + } + + /** + * Returns the entire matched namespace URI. If the pattern did not match, null is returned. + * + * @return matched namespace URI or null + */ + public String getNamespaceURI() { + if (!hasMatched()) { + return null; + } + if (namespaceURI == null) { + namespaceURI = getRegexMatchResult().group(0); + } + return namespaceURI; + } + + /** + * Returns the versionless namespace URI of the matched URI or null the underlying pattern was not matched. + * The versionless namespace URI may be used to identify a profile, independent of its version. + * + * @return profile-identifying, versionless namespace URI or null if there is no match + */ + public String getVersionlessNamespaceURI() { + if (!hasMatched()) { + return null; + } + if (versionlessNamespaceURI == null) { + versionlessNamespaceURI = String.join("", getNonGroups()); + } + return versionlessNamespaceURI; + } + + /** + * Returns the version of the matched namespace URI or null if the underlying pattern was not matched. + * The version string is formatted using the {@link #getVersionFormat() version format} provided when constructing this result. + * If no format was provided, the version parts get concatenated via comma. + * + * @return version string of the matched namespace URI or null if there is no match + */ + public String getVersion() { + if (!hasMatched()) { + return null; + } + if (version == null) { + if (versionFormat != null) { + version = versionFormat.format(getGroups()); + } else { + // by default just concatenate version parts via comma without the entire match (group 0) + version = String.join(",", Arrays.copyOfRange(getGroups(), 1, getGroups().length)); + } + } + return version; + } + + /** + * Returns the Regex match result produced from the matcher given in the constructor. + * If there was no match, the match result is null. + * + * @return match result or null if there is no match + */ + protected MatchResult getRegexMatchResult() { + return regexMatchResult; + } + + /** + * Returns all group strings of the underlying match result. If no match result is present, null is returned. + * The indices of the groups match the indices of the returned array. + * Please note that group zero (at index zero) holds the entire matched pattern. + * + * @return group strings or null + */ + protected String[] getGroups() { + if (!hasMatched()) { + return null; + } + if (groups == null) { + final int groupCount = getRegexMatchResult().groupCount(); + // group 0 has complete match and is not included in group count + groups = new String[groupCount + 1]; + for (int i = 0; i <= groupCount; i++) { + groups[i] = getRegexMatchResult().group(i); + } + } + return groups; + } + + /** + * Returns all non-group strings of the underlying match result. + * If no match result is present, null is returned. + * + * @return non-group strings or null + */ + protected String[] getNonGroups() { + if (!hasMatched()) { + return null; + } + if (nonGroups == null) { + final int groupCount = getRegexMatchResult().groupCount(); + final ArrayList<String> nonGroupList = new ArrayList<String>(); + String nonGroup; + int start = getRegexMatchResult().start(); + for (int i = 1; i <= groupCount; i++) { + nonGroup = getNamespaceURI().substring(start, getRegexMatchResult().start(i)); + if (!nonGroup.isEmpty()) { + nonGroupList.add(nonGroup); + } + start = getRegexMatchResult().end(i); + } + nonGroup = getNamespaceURI().substring(start, getRegexMatchResult().end()); + if (!nonGroup.isEmpty()) { + nonGroupList.add(nonGroup); + } + nonGroups = nonGroupList.toArray(new String[nonGroupList.size()]); + } + return nonGroups; + } + + @Override + public String toString() { + if (!hasMatched()) { + return "No match."; + } + return getVersionlessNamespaceURI() + " [" + getVersion() + "]"; + } +} diff --git a/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternRegistry.java b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternRegistry.java new file mode 100644 index 00000000000..d044c04b762 --- /dev/null +++ b/plugins/uml/org.eclipse.papyrus.uml.modelrepair/src/org/eclipse/papyrus/uml/modelrepair/internal/uripattern/ProfileNamespaceURIPatternRegistry.java @@ -0,0 +1,235 @@ +/******************************************************************************* + * Copyright (c) 2016 EclipseSource Services GmbH and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Martin Fleck - initial API and implementation + *******************************************************************************/ +package org.eclipse.papyrus.uml.modelrepair.internal.uripattern; + +import static org.eclipse.papyrus.uml.modelrepair.Activator.PLUGIN_ID; +import static org.eclipse.papyrus.uml.modelrepair.Activator.log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.Platform; + +import com.google.common.base.Optional; +import com.google.common.collect.Lists; + +/** + * This registry holds all profile namespace URI patterns that have been provided through the profile namespace + * URI pattern extension point or via the API of this registry. + * + * @author Martin Fleck <mfleck@eclipsesource.com> + */ +public class ProfileNamespaceURIPatternRegistry { + + /** Singleton instance of this registry */ + public static final ProfileNamespaceURIPatternRegistry INSTANCE = new ProfileNamespaceURIPatternRegistry(); + + /** ID under which extension points are registered */ + private static final String EXTENSION_ID = PLUGIN_ID + ".profileNamespaceURIPattern"; //$NON-NLS-1$ + + /** Name of the profile namespace URI pattern configuration element */ + private static final String NAME = "profileNamespaceURIPattern"; //$NON-NLS-1$ + + /** Attribute of the profile namespace URI pattern configuration element holding the URI pattern */ + private static final String URI_PATTERN = "uriPattern"; //$NON-NLS-1$ + + /** Attribute of the profile namespace URI pattern configuration element holding the version format */ + private static final String VERSION_FORMAT = "versionFormat"; //$NON-NLS-1$ + + /** List of registered profile namespace URI patterns */ + private List<ProfileNamespaceURIPattern> profileNamespaceURIPatterns = new ArrayList<ProfileNamespaceURIPattern>(); + + /** + * Private constructor to avoid creation in favor of singleton instance. + */ + private ProfileNamespaceURIPatternRegistry() { + initFromExtensionPoints(); + } + + /** + * Reads profile namespace URI patterns from the extension points and adds them to the list of patterns. + */ + private void initFromExtensionPoints() { + final IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(EXTENSION_ID); + + // read data from plugins + for (IConfigurationElement element : config) { + try { + if (NAME.equals(element.getName())) { + ProfileNamespaceURIPattern pattern = processProfileNamespaceURIPattern(element); + register(pattern); + } + } catch (Exception ex) { + log.error(ex); + } + } + } + + /** + * Reads the necessary data from the provided configuration element to create a new profile namespace URI pattern. + * + * @param element + * configuration element + * @return newly created profile namespace URI pattern + */ + private ProfileNamespaceURIPattern processProfileNamespaceURIPattern(IConfigurationElement element) { + final String uriPattern = element.getAttribute(URI_PATTERN); + final String versionFormat = element.getAttribute(VERSION_FORMAT); // is optional, may return null + return new ProfileNamespaceURIPattern(uriPattern, versionFormat); + } + + /** + * Returns an unmodifiable list of all registered profile namespace URI patterns. + * + * @return list of registered profile namespace URI patterns + */ + public List<ProfileNamespaceURIPattern> getProfileNamespaceURIPatterns() { + return Collections.unmodifiableList(profileNamespaceURIPatterns); + } + + /** + * Adds the provided profile namespace URI pattern to this registry. + * + * @param profileNamespaceURIPattern + * pattern to be registered + */ + public void register(ProfileNamespaceURIPattern profileNamespaceURIPattern) { + profileNamespaceURIPatterns.add(profileNamespaceURIPattern); + } + + /** + * Removes the given profile namespace URI patterns from this registry. + * If the pattern can not be found, the registry remains unchanged. + * + * @param profileNamespaceURIPattern + * pattern to be unregistered, if present + */ + public void unregister(ProfileNamespaceURIPattern profileNamespaceURIPattern) { + profileNamespaceURIPatterns.remove(profileNamespaceURIPattern); + } + + /** + * Returns an {@link Optional} containing the first pattern that matches the given uri, + * if such a pattern exists. + * + * @param namespaceURI + * namespace URI to be matched + * @return Optional containing the first matching pattern, if such a pattern exists + */ + public Optional<ProfileNamespaceURIPattern> tryFindPattern(String namespaceURI) { + for (ProfileNamespaceURIPattern pattern : getProfileNamespaceURIPatterns()) { + ProfileNamespaceURIPatternMatchResult match = pattern.match(namespaceURI); + if (match.hasMatched()) { + return Optional.of(pattern); + } + } + return Optional.absent(); + } + + /** + * Returns a list of patterns that match the given namespace URI. + * If no such pattern exist, an empty list is returned. + * + * @param namespaceURI + * namespace URI to be matched + * @return list of matching patterns + */ + public List<ProfileNamespaceURIPattern> findPatterns(String namespaceURI) { + List<ProfileNamespaceURIPattern> patterns = Lists.newArrayList(); + for (ProfileNamespaceURIPattern pattern : getProfileNamespaceURIPatterns()) { + ProfileNamespaceURIPatternMatchResult match = pattern.match(namespaceURI); + if (match.hasMatched()) { + patterns.add(pattern); + } + } + return patterns; + } + + /** + * Returns an {@link Optional} containing the first pattern match that matches the given namespace URI, + * if such a pattern match exists. + * + * @param namespaceURI + * namespace URI to be matched + * @return Optional containing the first matching pattern match, if such a pattern match exists + */ + public Optional<ProfileNamespaceURIPatternMatchResult> tryFindMatch(String namespaceURI) { + for (ProfileNamespaceURIPattern pattern : getProfileNamespaceURIPatterns()) { + ProfileNamespaceURIPatternMatchResult match = pattern.match(namespaceURI); + if (match.hasMatched()) { + return Optional.of(match); + } + } + return Optional.absent(); + } + + /** + * Returns a list of pattern matches that match the given namespace URI. + * If no such pattern match exist, an empty list is returned. + * + * @param namespaceURI + * namespace URI to be matched + * @return list of matching pattern matches + */ + public List<ProfileNamespaceURIPatternMatchResult> findMatches(String namespaceURI) { + List<ProfileNamespaceURIPatternMatchResult> matches = Lists.newArrayList(); + for (ProfileNamespaceURIPattern pattern : getProfileNamespaceURIPatterns()) { + ProfileNamespaceURIPatternMatchResult match = pattern.match(namespaceURI); + if (match.hasMatched()) { + matches.add(match); + } + } + return matches; + } + + /** + * Returns an {@link Optional} containing the first valid pattern comparison that matches both namespace URIs, + * if such a comparison exists. + * + * @param lhsNamespaceUri + * left-hand side namespace URI of the comparison + * @param rhsNamespaceUri + * right-hand side namespace URI of the comparison + * @return Optional containing the first valid pattern comparison, if such a comparison exists + */ + public Optional<ProfileNamespaceURIPatternComparison> tryFindComparison(String lhsNamespaceUri, String rhsNamespaceUri) { + for (ProfileNamespaceURIPattern pattern : getProfileNamespaceURIPatterns()) { + ProfileNamespaceURIPatternComparison comparison = pattern.compare(lhsNamespaceUri, rhsNamespaceUri); + if (comparison.isValid()) { + return Optional.of(comparison); + } + } + return Optional.absent(); + } + + /** + * Returns a list of valid pattern comparisons that match both namespace URIs. + * If no such comparison exist, an empty list is returned. + * + * @param lhsNamespaceUri + * left-hand side namespace URI of the comparison + * @param rhsNamespaceUri + * right-hand side namespace URI of the comparison + * @return list of valid comparisons between the given namespace URIs + */ + public List<ProfileNamespaceURIPatternComparison> findComparisons(String lhsNamespaceUri, String rhsNamespaceUri) { + List<ProfileNamespaceURIPatternComparison> comparisons = Lists.newArrayList(); + for (ProfileNamespaceURIPattern pattern : getProfileNamespaceURIPatterns()) { + ProfileNamespaceURIPatternComparison comparison = pattern.compare(lhsNamespaceUri, rhsNamespaceUri); + if (comparison.isValid()) { + comparisons.add(comparison); + } + } + return comparisons; + } +} |