Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPascal Rapicault2010-01-13 17:29:41 +0000
committerPascal Rapicault2010-01-13 17:29:41 +0000
commit18189f0d42f7375660762dc6c885cf31683ae562 (patch)
tree17775d847bed9a33f3c68b74db2df75a2139c0bc /bundles/org.eclipse.equinox.p2.metadata/src/org
parentc363f2984a09b73c422e38f4556fd3b23eafe958 (diff)
downloadrt.equinox.p2-18189f0d42f7375660762dc6c885cf31683ae562.tar.gz
rt.equinox.p2-18189f0d42f7375660762dc6c885cf31683ae562.tar.xz
rt.equinox.p2-18189f0d42f7375660762dc6c885cf31683ae562.zip
Merging api branch back to HEADv20100113
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.metadata/src/org')
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java15
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/BasicVersion.java83
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Copyright.java2
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/IRequiredCapability.java45
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnit.java132
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitFragment.java27
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitPatch.java36
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/License.java16
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Messages.java25
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/NotRequirement.java69
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ORRequirement.java85
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OSGiVersion.java213
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OmniVersion.java247
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ProvidedCapability.java13
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java247
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequirementChange.java43
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ResolvedInstallableUnit.java149
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointData.java18
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointInstruction.java18
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointType.java5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TranslationSupport.java299
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/UpdateDescriptor.java5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormat.java345
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormatParser.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormat.java)963
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionParser.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionParser.java)108
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionVector.java)314
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionedId.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionedId.java)5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/All.java42
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/And.java50
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/At.java96
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Binary.java135
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java392
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CollectionFilter.java85
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Compare.java60
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Equals.java52
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/EvaluationContext.java161
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Exists.java42
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java341
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/ExpressionFactory.java125
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IExpressionConstants.java58
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IRepeatableIterator.java25
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPApproximation.java77
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPFilter.java69
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LambdaExpression.java72
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Literal.java123
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MatchExpression.java83
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Matches.java95
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Member.java178
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MemberProvider.java139
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/NAry.java59
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Not.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Or.java50
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Parameter.java64
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/RepeatableIterator.java237
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Unary.java57
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Variable.java79
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/ExpressionParser.java617
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/LDAPFilterParser.java268
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/messages.properties13
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/IUPropertyQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IUPropertyQuery.java)12
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/LatestIUVersionQuery.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/ObjectMatchQuery.java22
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/UpdateQuery.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/query/QueryHelpers.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/QueryHelpers.java)15
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnit.java278
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequiredCapability.java90
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/MetadataFactory.java170
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/Version.java504
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormatParser.java530
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CapabilityQuery.java61
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/LatestIUVersionQuery.java51
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/UpdateQuery.java38
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IArtifactKey.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IArtifactKey.java)16
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ICopyright.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ICopyright.java)5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnit.java293
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnitFragment.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnitFragment.java)5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnitPatch.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnitPatch.java)11
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ILicense.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ILicense.java)8
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IProvidedCapability.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IProvidedCapability.java)24
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirement.java38
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirementChange.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequirementChange.java)5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointData.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointData.java)6
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointInstruction.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointInstruction.java)3
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointType.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointType.java)4
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IUpdateDescriptor.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IUpdateDescriptor.java)4
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionFormat.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionedId.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IVersionedId.java)5
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/Version.java229
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/VersionFormatException.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/FormatException.java)10
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/VersionRange.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionRange.java)97
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionParseException.java28
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionUtil.java179
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IEvaluationContext.java39
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpression.java67
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionFactory.java206
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionParser.java26
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionVisitor.java26
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IFilterExpression.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IMatchExpression.java53
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/SimplePattern.java159
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryMemberQuery.java56
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryQuery.java44
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/ExpressionQuery.java46
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/FragmentQuery.java48
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/GroupQuery.java44
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/InstallableUnitQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/InstallableUnitQuery.java)17
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/PatchQuery.java44
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/Collector.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/Collector.java)75
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/CompoundQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompoundQuery.java)136
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/CompoundQueryable.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompoundQueryable.java)83
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ContextQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/ContextQuery.java)13
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ICompositeQuery.java27
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IMatchQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IMatchQuery.java)12
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/Query.java)12
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java57
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryable.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IQueryable.java)11
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/LimitQuery.java57
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/MatchQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/MatchQuery.java)18
-rw-r--r--bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/PipedQuery.java (renamed from bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompositeQuery.java)48
119 files changed, 8928 insertions, 2948 deletions
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java
index f495ee2a7..709fa2b1c 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ArtifactKey.java
@@ -10,11 +10,12 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
+import org.eclipse.equinox.p2.metadata.Version;
+
import java.util.ArrayList;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.Assert;
-import org.eclipse.equinox.internal.provisional.p2.metadata.IArtifactKey;
-import org.eclipse.equinox.internal.provisional.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.IArtifactKey;
/**
* The concrete type for representing IArtifactKey's.
@@ -31,7 +32,7 @@ public class ArtifactKey implements IArtifactKey {
private static String[] getArrayFromList(String stringList, String separator) {
if (stringList == null || stringList.trim().length() == 0)
return new String[0];
- ArrayList list = new ArrayList();
+ ArrayList<String> list = new ArrayList<String>();
boolean separatorSeen = true;
StringTokenizer tokens = new StringTokenizer(stringList, separator, true);
while (tokens.hasMoreTokens()) {
@@ -48,7 +49,7 @@ public class ArtifactKey implements IArtifactKey {
}
if (separatorSeen)
list.add(""); //$NON-NLS-1$
- return (String[]) list.toArray(new String[list.size()]);
+ return list.toArray(new String[list.size()]);
}
public static IArtifactKey parse(String specification) {
@@ -79,6 +80,12 @@ public class ArtifactKey implements IArtifactKey {
this.version = version;
}
+ public ArtifactKey(IArtifactKey artifactKey) {
+ this.classifier = artifactKey.getClassifier();
+ this.id = artifactKey.getId();
+ this.version = artifactKey.getVersion();
+ }
+
public String getClassifier() {
return classifier;
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/BasicVersion.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/BasicVersion.java
new file mode 100644
index 000000000..0514284f0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/BasicVersion.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata;
+
+import org.eclipse.equinox.p2.metadata.Version;
+
+/**
+ * The abstract BasicVersion class adds the methods necessary to to compare and serialize
+ * versions in version ranges. The class is not intended as public API.
+ */
+public abstract class BasicVersion extends Version {
+ private static final long serialVersionUID = -2983093417537485027L;
+
+ /**
+ * Returns the OSGi major component of this version identifier.
+ *
+ * @return The major component.
+ * @throws UnsupportedOperationException if the first element in the
+ * vector is not a number.
+ * @see #isOSGiCompatible()
+ */
+ public abstract int getMajor();
+
+ /**
+ * Returns the OSGi micro component of this version identifier.
+ *
+ * @return The micro component.
+ * @throws UnsupportedOperationException if the third element in the
+ * vector is not a number.
+ * @see #isOSGiCompatible()
+ */
+ public abstract int getMicro();
+
+ /**
+ * Returns the OSGi minor component of this version identifier.
+ *
+ * @return The minor component.
+ * @throws UnsupportedOperationException if the second element in the
+ * vector is not a number.
+ * @see #isOSGiCompatible()
+ */
+ public abstract int getMinor();
+
+ /**
+ * Returns the OSGi qualifier component of this version identifier.
+ *
+ * @return The qualifier component or <code>null</code> if not set.
+ * @throws UnsupportedOperationException if the fourth element in the
+ * vector is set to something other then a string.
+ * @see #isOSGiCompatible()
+ */
+ public abstract String getQualifier();
+
+ /**
+ * Appends the original for this version onto the <code>sb</code> StringBuffer
+ * if present.
+ * @param sb The buffer that will receive the raw string format
+ * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped
+ */
+ public abstract void originalToString(StringBuffer sb, boolean rangeSafe);
+
+ /**
+ * Appends the raw format for this version onto the <code>sb</code> StringBuffer.
+ * @param sb The buffer that will receive the raw string format
+ * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped
+ */
+ public abstract void rawToString(StringBuffer sb, boolean rangeSafe);
+
+ /**
+ * This method is package protected since it violates the immutable
+ * contract.
+ * @return The raw vector. Must be treated as read-only
+ */
+ abstract Comparable<?>[] getVector();
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Copyright.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Copyright.java
index 9098f377c..b56578830 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Copyright.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Copyright.java
@@ -11,7 +11,7 @@
package org.eclipse.equinox.internal.p2.metadata;
import java.net.URI;
-import org.eclipse.equinox.internal.provisional.p2.metadata.ICopyright;
+import org.eclipse.equinox.p2.metadata.ICopyright;
/**
* The <code>Copyright</code> class represents a software copyright. A copyright has
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/IRequiredCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/IRequiredCapability.java
new file mode 100644
index 000000000..ef92ee045
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/IRequiredCapability.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+* Copyright (c) 2008, 2009 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
+*
+* Contributors:
+* EclipseSource - initial API and implementation
+******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata;
+
+import org.eclipse.equinox.p2.metadata.*;
+
+/**
+ * A required capability represents some external constraint on an {@link IInstallableUnit}.
+ * Each capability represents something an {@link IInstallableUnit} needs that
+ * it expects to be provided by another {@link IInstallableUnit}. Capabilities are
+ * entirely generic, and are intended to be capable of representing anything that
+ * an {@link IInstallableUnit} may need either at install time, or at runtime.
+ * <p>
+ * Capabilities are segmented into namespaces. Anyone can introduce new
+ * capability namespaces. Some well-known namespaces are introduced directly
+ * by the provisioning framework.
+ *
+ * @see IInstallableUnit#NAMESPACE_IU_ID
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ */
+public interface IRequiredCapability extends IRequirement {
+
+ // public String getFilter();
+
+ public String getName();
+
+ public String getNamespace();
+
+ /**
+ * Returns the range of versions that satisfy this required capability. Returns
+ * an empty version range ({@link VersionRange#emptyRange} if any version
+ * will satisfy the capability.
+ * @return the range of versions that satisfy this required capability.
+ */
+ public VersionRange getRange();
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnit.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnit.java
index b5dd1ac29..018f26cb6 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnit.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnit.java
@@ -11,40 +11,45 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import java.util.ArrayList;
-import java.util.Map;
+import org.eclipse.equinox.p2.metadata.Version;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
+import org.osgi.framework.Filter;
public class InstallableUnit implements IInstallableUnit {
private static final OrderedProperties NO_PROPERTIES = new OrderedProperties();
private static final IProvidedCapability[] NO_PROVIDES = new IProvidedCapability[0];
- private static final IRequiredCapability[] NO_REQUIRES = new IRequiredCapability[0];
+ private static final IRequirement[] NO_REQUIRES = new IRequirement[0];
private static final IArtifactKey[] NO_ARTIFACTS = new IArtifactKey[0];
private static final ITouchpointData[] NO_TOUCHPOINT_DATA = new ITouchpointData[0];
+ private static final ILicense[] NO_LICENSE = new ILicense[0];
private IArtifactKey[] artifacts = NO_ARTIFACTS;
- private String filter;
+ private Filter filter;
private String id;
private OrderedProperties properties;
private OrderedProperties localizedProperties;
IProvidedCapability[] providedCapabilities = NO_PROVIDES;
- private IRequiredCapability[] requires = NO_REQUIRES;
- private IRequiredCapability[] metaRequires = NO_REQUIRES;
+ private IRequirement[] requires = NO_REQUIRES;
+ private IRequirement[] metaRequires = NO_REQUIRES;
private boolean singleton;
- private ArrayList touchpointData = null;
+ private ITouchpointData[] touchpointData = NO_TOUCHPOINT_DATA;
private ITouchpointType touchpointType;
- private Version version;
+ private Version version = Version.emptyVersion;
private IUpdateDescriptor updateInfo;
- private ILicense license;
+ private ILicense[] licenses = NO_LICENSE;
private ICopyright copyright;
public InstallableUnit() {
@@ -52,26 +57,22 @@ public class InstallableUnit implements IInstallableUnit {
}
public void addTouchpointData(ITouchpointData newData) {
- ensureTouchpointDataCapacity(1);
- touchpointData.add(newData);
- }
-
- public int compareTo(Object toCompareTo) {
- if (!(toCompareTo instanceof IInstallableUnit)) {
- return -1;
+ int tl = touchpointData.length;
+ if (tl == 0)
+ touchpointData = new ITouchpointData[] {newData};
+ else {
+ ITouchpointData[] newDatas = new ITouchpointData[tl + 1];
+ System.arraycopy(touchpointData, 0, newDatas, 0, tl);
+ newDatas[tl] = newData;
+ touchpointData = newDatas;
}
- IInstallableUnit other = (IInstallableUnit) toCompareTo;
- if (getId().compareTo(other.getId()) == 0)
- return (getVersion().compareTo(other.getVersion()));
- return getId().compareTo(other.getId());
}
- private void ensureTouchpointDataCapacity(int size) {
- if (touchpointData != null) {
- touchpointData.ensureCapacity(size);
- } else {
- touchpointData = new ArrayList(size);
- }
+ public int compareTo(IInstallableUnit other) {
+ int cmp = getId().compareTo(other.getId());
+ if (cmp == 0)
+ cmp = getVersion().compareTo(other.getVersion());
+ return cmp;
}
public boolean equals(Object obj) {
@@ -95,16 +96,16 @@ public class InstallableUnit implements IInstallableUnit {
return true;
}
- public IArtifactKey[] getArtifacts() {
- return artifacts;
+ public Collection<IArtifactKey> getArtifacts() {
+ return CollectionUtils.unmodifiableList(artifacts);
}
- public String getFilter() {
+ public Filter getFilter() {
return filter;
}
- public IInstallableUnitFragment[] getFragments() {
- return null;
+ public List<IInstallableUnitFragment> getFragments() {
+ return CollectionUtils.emptyList();
}
public String getId() {
@@ -117,7 +118,7 @@ public class InstallableUnit implements IInstallableUnit {
*
* @return an <i>unmodifiable copy</i> of the IU properties.
*/
- public Map getProperties() {
+ public Map<String, String> getProperties() {
return OrderedProperties.unmodifiableProperties(properties());
}
@@ -135,18 +136,21 @@ public class InstallableUnit implements IInstallableUnit {
return properties().getProperty(key);
}
- public IProvidedCapability[] getProvidedCapabilities() {
- return providedCapabilities;
+ public Collection<IProvidedCapability> getProvidedCapabilities() {
+ return CollectionUtils.unmodifiableList(providedCapabilities);
}
- public IRequiredCapability[] getRequiredCapabilities() {
- return requires;
+ public String getProperty(String key, String locale) {
+ return TranslationSupport.getInstance().getIUProperty(this, key, locale);
+ }
+
+ public List<IRequirement> getRequiredCapabilities() {
+ return CollectionUtils.unmodifiableList(requires);
}
- public ITouchpointData[] getTouchpointData() {
- return (touchpointData == null ? NO_TOUCHPOINT_DATA //
- : (ITouchpointData[]) touchpointData.toArray(new ITouchpointData[touchpointData.size()]));
+ public List<ITouchpointData> getTouchpointData() {
+ return CollectionUtils.unmodifiableList(touchpointData);
}
public ITouchpointType getTouchpointType() {
@@ -165,10 +169,6 @@ public class InstallableUnit implements IInstallableUnit {
return result;
}
- public boolean isFragment() {
- return false;
- }
-
public boolean isResolved() {
return false;
}
@@ -195,10 +195,14 @@ public class InstallableUnit implements IInstallableUnit {
providedCapabilities = newCapabilities;
}
- public void setFilter(String filter) {
+ public void setFilter(Filter filter) {
this.filter = filter;
}
+ public void setFilter(String filter) {
+ setFilter(filter == null ? null : ExpressionUtil.parseLDAP(filter));
+ }
+
public void setId(String id) {
this.id = id;
}
@@ -209,7 +213,7 @@ public class InstallableUnit implements IInstallableUnit {
public String setLocalizedProperty(String key, String value) {
if (localizedProperties == null)
localizedProperties = new OrderedProperties();
- return (String) localizedProperties.put(key, value);
+ return localizedProperties.put(key, value);
}
public String setProperty(String key, String value) {
@@ -220,12 +224,12 @@ public class InstallableUnit implements IInstallableUnit {
return (String) properties.setProperty(key, value);
}
- public void setRequiredCapabilities(IRequiredCapability[] capabilities) {
+ public void setRequiredCapabilities(IRequirement[] capabilities) {
if (capabilities.length == 0) {
this.requires = NO_REQUIRES;
} else {
//copy array for safety
- this.requires = (IRequiredCapability[]) capabilities.clone();
+ this.requires = capabilities.clone();
}
}
@@ -257,12 +261,16 @@ public class InstallableUnit implements IInstallableUnit {
this.updateInfo = updateInfo;
}
- public void setLicense(ILicense license) {
- this.license = license;
+ public void setLicenses(ILicense[] license) {
+ this.licenses = license == null ? NO_LICENSE : license;
}
- public ILicense getLicense() {
- return license;
+ public Collection<ILicense> getLicenses() {
+ return CollectionUtils.unmodifiableList(licenses);
+ }
+
+ public ILicense[] getLicenses(String locale) {
+ return TranslationSupport.getInstance().getLicenses(this, locale);
}
public void setCopyright(ICopyright copyright) {
@@ -273,24 +281,24 @@ public class InstallableUnit implements IInstallableUnit {
return copyright;
}
- public boolean satisfies(IRequiredCapability candidate) {
- IProvidedCapability[] provides = getProvidedCapabilities();
- for (int i = 0; i < provides.length; i++)
- if (provides[i].satisfies(candidate))
- return true;
- return false;
+ public ICopyright getCopyright(String locale) {
+ return TranslationSupport.getInstance().getCopyright(this, locale);
+ }
+
+ public boolean satisfies(IRequirement candidate) {
+ return candidate.isMatch(this);
}
- public IRequiredCapability[] getMetaRequiredCapabilities() {
- return metaRequires;
+ public Collection<IRequirement> getMetaRequiredCapabilities() {
+ return CollectionUtils.unmodifiableList(metaRequires);
}
- public void setMetaRequiredCapabilities(IRequiredCapability[] metaReqs) {
+ public void setMetaRequiredCapabilities(IRequirement[] metaReqs) {
if (metaReqs.length == 0) {
this.metaRequires = NO_REQUIRES;
} else {
//copy array for safety
- this.metaRequires = (IRequiredCapability[]) metaReqs.clone();
+ this.metaRequires = metaReqs.clone();
}
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitFragment.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitFragment.java
index 7709c9dbc..86ea33ad2 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitFragment.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitFragment.java
@@ -10,37 +10,36 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnitFragment;
-import org.eclipse.equinox.internal.provisional.p2.metadata.IRequiredCapability;
+import java.util.List;
+import org.eclipse.equinox.p2.metadata.IInstallableUnitFragment;
+import org.eclipse.equinox.p2.metadata.IRequirement;
public class InstallableUnitFragment extends InstallableUnit implements IInstallableUnitFragment {
- private IRequiredCapability[] hostRequirements;
+ private IRequirement[] hostRequirements;
public InstallableUnitFragment() {
super();
}
- public void setHost(IRequiredCapability[] hostRequirements) {
+ public void setHost(IRequirement[] hostRequirements) {
if (hostRequirements == null)
return;
this.hostRequirements = hostRequirements;
addRequiredCapability(hostRequirements);
}
- private void addRequiredCapability(IRequiredCapability[] toAdd) {
- IRequiredCapability[] current = super.getRequiredCapabilities();
- IRequiredCapability[] result = new IRequiredCapability[current.length + toAdd.length];
- System.arraycopy(current, 0, result, 0, current.length);
- System.arraycopy(toAdd, 0, result, current.length, toAdd.length);
+ private void addRequiredCapability(IRequirement[] toAdd) {
+ List<IRequirement> current = super.getRequiredCapabilities();
+ int currSize = current.size();
+ IRequirement[] result = new IRequirement[currSize + toAdd.length];
+ for (int i = 0; i < currSize; ++i)
+ result[i] = current.get(i);
+ System.arraycopy(toAdd, 0, result, current.size(), toAdd.length);
setRequiredCapabilities(result);
}
- public boolean isFragment() {
- return true;
- }
-
- public IRequiredCapability[] getHost() {
+ public IRequirement[] getHost() {
return hostRequirements;
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitPatch.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitPatch.java
index 31013548b..41ce8f56a 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitPatch.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/InstallableUnitPatch.java
@@ -8,42 +8,46 @@
******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import java.util.List;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
+import org.eclipse.equinox.p2.metadata.*;
public class InstallableUnitPatch extends InstallableUnit implements IInstallableUnitPatch {
private IRequirementChange[] changes;
- private IRequiredCapability lifeCycle;
- private IRequiredCapability[][] scope;
-
- private void addRequiredCapability(IRequiredCapability[] toAdd) {
- IRequiredCapability[] current = super.getRequiredCapabilities();
- IRequiredCapability[] result = new IRequiredCapability[current.length + toAdd.length];
- System.arraycopy(current, 0, result, 0, current.length);
- System.arraycopy(toAdd, 0, result, current.length, toAdd.length);
+ private IRequirement lifeCycle;
+ private IRequirement[][] scope;
+
+ private void addRequiredCapability(IRequirement[] toAdd) {
+ List<IRequirement> current = super.getRequiredCapabilities();
+ int currSize = current.size();
+ IRequirement[] result = new IRequirement[currSize + toAdd.length];
+ for (int i = 0; i < currSize; ++i)
+ result[i] = current.get(i);
+ System.arraycopy(toAdd, 0, result, current.size(), toAdd.length);
setRequiredCapabilities(result);
}
- public IRequiredCapability[][] getApplicabilityScope() {
+ public IRequirement[][] getApplicabilityScope() {
return scope;
}
- public IRequiredCapability getLifeCycle() {
+ public IRequirement getLifeCycle() {
return lifeCycle;
}
- public IRequirementChange[] getRequirementsChange() {
- return changes;
+ public List<IRequirementChange> getRequirementsChange() {
+ return CollectionUtils.unmodifiableList(changes);
}
- public void setApplicabilityScope(IRequiredCapability[][] applyTo) {
+ public void setApplicabilityScope(IRequirement[][] applyTo) {
scope = applyTo;
}
- public void setLifeCycle(IRequiredCapability lifeCycle) {
+ public void setLifeCycle(IRequirement lifeCycle) {
if (lifeCycle == null)
return;
this.lifeCycle = lifeCycle;
- addRequiredCapability(new IRequiredCapability[] {lifeCycle});
+ addRequiredCapability(new IRequirement[] {lifeCycle});
}
public void setRequirementsChange(IRequirementChange[] changes) {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/License.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/License.java
index 112956f26..38c62c1b1 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/License.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/License.java
@@ -16,7 +16,7 @@ import java.math.BigInteger;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import org.eclipse.equinox.internal.provisional.p2.metadata.ILicense;
+import org.eclipse.equinox.p2.metadata.ILicense;
/**
* The <code>License</code> class represents a software license. A license has required body text
@@ -38,7 +38,7 @@ public class License implements ILicense {
/**
* The <code>digest</code> is the cached message digest of the normalized body
*/
- private BigInteger digest;
+ private String digest;
/**
* Creates a new license object which is identified by users using the <code>body</code> field.
@@ -49,11 +49,12 @@ public class License implements ILicense {
* @param body the license body, cannot be <code>null</code>
* @throws IllegalArgumentException when the <code>body</code> is <code>null</code>
*/
- public License(URI location, String body) {
+ public License(URI location, String body, String uuid) {
if (body == null)
throw new IllegalArgumentException("body cannot be null"); //$NON-NLS-1$
this.body = body;
this.location = location;
+ this.digest = uuid;
}
/**
@@ -78,9 +79,10 @@ public class License implements ILicense {
* version of the license where all whitespace has been reduced to one space.
* @return the message digest as a <code>BigInteger</code>, never <code>null</code>
*/
- public synchronized BigInteger getDigest() {
+ public synchronized String getUUID() {
if (digest == null)
- digest = calculateLicenseDigest();
+ digest = calculateLicenseDigest().toString(16);
+
return digest;
}
@@ -94,7 +96,7 @@ public class License implements ILicense {
return false;
if (obj instanceof ILicense) {
ILicense other = (ILicense) obj;
- if (other.getDigest().equals(getDigest()))
+ if (other.getUUID().equals(getUUID()))
return true;
}
return false;
@@ -104,7 +106,7 @@ public class License implements ILicense {
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
- return getDigest().hashCode();
+ return getUUID().hashCode();
}
private BigInteger calculateLicenseDigest() {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Messages.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Messages.java
index 10a596c32..0466d2676 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Messages.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/Messages.java
@@ -16,9 +16,6 @@ import org.eclipse.osgi.util.NLS;
* TODO Shouldn't be a public class in an API package.
*/
public class Messages extends NLS {
- public static String _0_is_not_a_positive_integer_in_osgi_1;
-
- public static String _0_is_not_a_string_in_osgi_1;
public static String _0_is_not_a_valid_qualifier_in_osgi_1;
@@ -54,6 +51,22 @@ public class Messages extends NLS {
public static String expected_slash_after_raw_vector_0;
+ public static String filter_trailing_characters;
+
+ public static String filter_missing_leftparen;
+
+ public static String filter_missing_rightparen;
+
+ public static String filter_invalid_operator;
+
+ public static String filter_missing_attr;
+
+ public static String filter_invalid_value;
+
+ public static String filter_missing_value;
+
+ public static String filter_premature_end;
+
public static String format_0_unable_to_parse_1;
public static String format_0_unable_to_parse_empty_version;
@@ -68,8 +81,6 @@ public class Messages extends NLS {
public static String illegal_character_encountered_ascii_0;
- public static String illegal_number_of_entries_0_in_osgi_1;
-
public static String missing_comma_in_range_0;
public static String negative_character_range;
@@ -80,14 +91,14 @@ public class Messages extends NLS {
public static String only_format_specified_0;
+ public static String only_max_and_empty_string_defaults_can_have_translations;
+
public static String original_must_start_with_colon_0;
public static String original_stated_but_missing_0;
public static String pad_defined_more_then_once;
- public static String pad_not_allowed_in_osgi_0;
-
public static String performing_subquery;
public static String premature_end_of_format;
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/NotRequirement.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/NotRequirement.java
deleted file mode 100644
index 752d5444e..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/NotRequirement.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.eclipse.equinox.internal.p2.metadata;
-
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
-
-public class NotRequirement implements IRequiredCapability {
- private IRequiredCapability negatedRequirement;
-
- public NotRequirement(IRequiredCapability iRequiredCapabilities) {
- negatedRequirement = iRequiredCapabilities;
- }
-
- public IRequiredCapability getRequirement() {
- return negatedRequirement;
- }
-
- public String getFilter() {
- return negatedRequirement.getFilter();
- }
-
- public String getName() {
- return negatedRequirement.getName();
- }
-
- public String getNamespace() {
- return negatedRequirement.getNamespace();
- }
-
- public VersionRange getRange() {
- return negatedRequirement.getRange();
- }
-
- public String[] getSelectors() {
- return negatedRequirement.getSelectors();
- }
-
- public boolean isGreedy() {
- return negatedRequirement.isGreedy();
- }
-
- public boolean isMultiple() {
- return negatedRequirement.isMultiple();
- }
-
- public boolean isOptional() {
- return negatedRequirement.isOptional();
- }
-
- public void setFilter(String filter) {
- // TODO Auto-generated method stub
-
- }
-
- public void setSelectors(String[] selectors) {
- // TODO Auto-generated method stub
-
- }
-
- public boolean isNegation() {
- return true;
- }
-
- public String toString() {
- return "NOT(" + negatedRequirement.toString() + ')'; //$NON-NLS-1$
- }
-
- public boolean satisfiedBy(IProvidedCapability cap) {
- return !negatedRequirement.satisfiedBy(cap);
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ORRequirement.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ORRequirement.java
deleted file mode 100644
index 3aab2c9ff..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ORRequirement.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.eclipse.equinox.internal.p2.metadata;
-
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
-
-public class ORRequirement implements IRequiredCapability {
- private IRequiredCapability[] oredRequirements;
-
- public ORRequirement(IRequiredCapability[] reqs) {
- oredRequirements = reqs;
- }
-
- public IRequiredCapability[] getRequirements() {
- return oredRequirements;
- }
-
- public String getFilter() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public String getName() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public String getNamespace() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public VersionRange getRange() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public String[] getSelectors() {
- // TODO Auto-generated method stub
- return null;
- }
-
- public boolean isGreedy() {
- // TODO Auto-generated method stub
- return true;
- }
-
- public boolean isMultiple() {
- // TODO Auto-generated method stub
- return false;
- }
-
- public boolean isOptional() {
- return false;
- }
-
- public void setFilter(String filter) {
- // TODO Auto-generated method stub
-
- }
-
- public void setSelectors(String[] selectors) {
- // TODO Auto-generated method stub
-
- }
-
- public boolean isNegation() {
- return false;
- }
-
- public String toString() {
- String result = "OR(";
- for (int i = 0; i < oredRequirements.length; i++) {
- result += oredRequirements[i].toString();
- }
- return result + ")";
- }
-
- public boolean satisfiedBy(IProvidedCapability cap) {
- for (int i = 0; i < oredRequirements.length; i++) {
- boolean result = oredRequirements[i].satisfiedBy(cap);
- if (result)
- return true;
- }
- return false;
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OSGiVersion.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OSGiVersion.java
new file mode 100644
index 000000000..979494d49
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OSGiVersion.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata;
+
+import org.eclipse.equinox.p2.metadata.IVersionFormat;
+import org.eclipse.equinox.p2.metadata.Version;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * @Immutable
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class OSGiVersion extends BasicVersion {
+
+ private static final long serialVersionUID = -4530178927569560877L;
+
+ private static final boolean[] allowedOSGiChars;
+
+ private final int major;
+
+ private final int minor;
+
+ private final int micro;
+
+ private final Comparable<?> qualifier;
+
+ static {
+ allowedOSGiChars = new boolean[128];
+ for (int c = '0'; c <= '9'; ++c)
+ allowedOSGiChars[c] = true;
+ for (int c = 'A'; c <= 'Z'; ++c)
+ allowedOSGiChars[c] = true;
+ for (int c = 'a'; c <= 'z'; ++c)
+ allowedOSGiChars[c] = true;
+ allowedOSGiChars['_'] = true;
+ allowedOSGiChars['-'] = true;
+ }
+
+ public static boolean isValidOSGiQualifier(Comparable<?> e) {
+ if (e == VersionVector.MAXS_VALUE)
+ return true;
+
+ if (!(e instanceof String))
+ return false;
+
+ String s = (String) e;
+ int idx = s.length();
+ boolean[] allowed = allowedOSGiChars;
+ while (--idx >= 0) {
+ int c = s.charAt(idx);
+ if (c < '-' || c > 'z' || !allowed[c])
+ return false;
+ }
+ return true;
+ }
+
+ static BasicVersion fromVector(Comparable<?>[] vector, Comparable<? extends Object> pad) {
+ if (vector.length != 4) {
+ if (vector.length == 0) {
+ if (pad == null)
+ return (BasicVersion) emptyVersion;
+ if (pad == VersionVector.MAX_VALUE)
+ return (BasicVersion) MAX_VERSION;
+ }
+ throw new IllegalArgumentException();
+ }
+ int major = ((Integer) vector[0]).intValue();
+ int minor = ((Integer) vector[1]).intValue();
+ int micro = ((Integer) vector[2]).intValue();
+ Comparable<?> qualifier = vector[3];
+ return (major == 0 && minor == 0 && micro == 0 && qualifier == VersionVector.MINS_VALUE) ? (BasicVersion) emptyVersion : new OSGiVersion(major, minor, micro, qualifier);
+ }
+
+ public OSGiVersion(int major, int minor, int micro, Comparable<? extends Object> qualifier) {
+ this.major = major;
+ this.minor = minor;
+ this.micro = micro;
+ if (!isValidOSGiQualifier(qualifier))
+ throw new IllegalArgumentException(NLS.bind(Messages._0_is_not_a_valid_qualifier_in_osgi_1, "qualifier", this)); //$NON-NLS-1$
+ this.qualifier = qualifier;
+ }
+
+ public int compareTo(Version v) {
+ int result;
+ if (!(v instanceof OSGiVersion)) {
+ BasicVersion ov = (BasicVersion) v;
+ result = VersionVector.compare(getVector(), null, ov.getVector(), ov.getPad());
+ } else {
+ OSGiVersion ov = (OSGiVersion) v;
+ result = major - ov.major;
+ if (result == 0) {
+ result = minor - ov.minor;
+ if (result == 0) {
+ result = micro - ov.micro;
+ if (result == 0)
+ result = VersionVector.compareSegments(qualifier, ov.qualifier);
+ }
+ }
+ }
+ return result;
+ }
+
+ public boolean equals(Object object) {
+ if (object == this)
+ return true;
+
+ if (!(object instanceof OSGiVersion)) {
+ if (object instanceof BasicVersion) {
+ BasicVersion ov = (BasicVersion) object;
+ return VersionVector.equals(getVector(), null, ov.getVector(), ov.getPad());
+ }
+ return false;
+ }
+
+ OSGiVersion other = (OSGiVersion) object;
+ return micro == other.micro && minor == other.minor && major == other.major && qualifier.equals(other.qualifier);
+ }
+
+ public IVersionFormat getFormat() {
+ return VersionFormat.OSGI_FORMAT;
+ }
+
+ public int getMajor() {
+ return major;
+ }
+
+ public int getMicro() {
+ return micro;
+ }
+
+ public int getMinor() {
+ return minor;
+ }
+
+ public String getOriginal() {
+ return toString();
+ }
+
+ public String getQualifier() {
+ return qualifier == VersionVector.MAXS_VALUE ? IVersionFormat.DEFAULT_MAX_STRING_TRANSLATION : (String) qualifier;
+ }
+
+ public int hashCode() {
+ return (major << 24) + (minor << 16) + (micro << 8) + qualifier.hashCode();
+ }
+
+ public boolean isOSGiCompatible() {
+ return true;
+ }
+
+ public void originalToString(StringBuffer sb, boolean rangeSafe) {
+ toString(sb);
+ }
+
+ public void rawToString(StringBuffer sb, boolean rangeSafe) {
+ sb.append(major);
+ sb.append('.');
+ sb.append(minor);
+ sb.append('.');
+ sb.append(micro);
+ sb.append('.');
+ sb.append('\'');
+ sb.append(qualifier);
+ sb.append('\'');
+ }
+
+ public void toString(StringBuffer sb) {
+ sb.append(major);
+ sb.append('.');
+ sb.append(minor);
+ sb.append('.');
+ sb.append(micro);
+ if (qualifier != VersionVector.MINS_VALUE) {
+ sb.append('.');
+ sb.append(getQualifier());
+ }
+ }
+
+ public Comparable<?>[] getVector() {
+ return new Comparable[] {VersionParser.valueOf(major), VersionParser.valueOf(minor), VersionParser.valueOf(micro), qualifier};
+ }
+
+ public Comparable<?> getPad() {
+ return null;
+ }
+
+ public Comparable<?> getSegment(int index) {
+ switch (index) {
+ case 0 :
+ return VersionParser.valueOf(major);
+ case 1 :
+ return VersionParser.valueOf(minor);
+ case 2 :
+ return VersionParser.valueOf(micro);
+ case 3 :
+ return qualifier;
+ }
+ throw new ArrayIndexOutOfBoundsException(index); // Not in the imaginary vector array
+ }
+
+ public int getSegmentCount() {
+ return 4;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OmniVersion.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OmniVersion.java
new file mode 100644
index 000000000..c45171038
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/OmniVersion.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata;
+
+import org.eclipse.equinox.p2.metadata.IVersionFormat;
+import org.eclipse.equinox.p2.metadata.Version;
+
+/**
+ * <p>The Generic Omni Version is composed of a vector of Comparable objects and a pad value. The pad
+ * might be <code>null</code>. The vector can contain integers, strings, {@link VersionVector}
+ * instances, or one of the special objects {@link VersionVector#MAX_VALUE MAX_VALUE},
+ * {@link VersionVector#MAXS_VALUE MAXS_VALUE}, or {@link VersionVector#MIN_VALUE MIN_VALUE}.</p>
+ *
+ * <p>When two versions are compared, they are always considered padded to infinity by their
+ * pad value or by {@link VersionVector#MIN_VALUE MIN_VALUE} in case the pad value is
+ * <code>null</code>. The comparison is type sensitive so that:</p><pre>
+ * MAX_VALUE &gt; Integer &gt; VersionVector &gt; MAXS_VALUE &gt; String &gt; MIN_VALUE<br/>
+ * </pre>
+ *
+ * @Immutable
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class OmniVersion extends BasicVersion {
+ private static final long serialVersionUID = 1996212688810048879L;
+
+ private static OmniVersion minimumVersion;
+
+ private static OmniVersion maximumVersion;
+
+ private final Comparable<?>[] vector;
+
+ private final Comparable<?> padValue;
+
+ /**
+ * The optional format
+ */
+ private final IVersionFormat format;
+
+ /**
+ * The optional original string
+ */
+ private final String original;
+
+ static BasicVersion fromVector(Comparable<?>[] vector, Comparable<?> padValue, IVersionFormat format, String original) {
+ if (vector.length == 0) {
+ if (padValue == null)
+ return (BasicVersion) emptyVersion;
+ if (padValue == VersionVector.MAX_VALUE)
+ return (BasicVersion) MAX_VERSION;
+ }
+ if (vector.length == 3 && padValue == null && vector[0] == VersionParser.ZERO_INT && vector[1] == VersionParser.ZERO_INT && vector[2] == VersionParser.ZERO_INT)
+ return (BasicVersion) emptyVersion;
+
+ return new OmniVersion(vector, padValue, format, original);
+ }
+
+ public static Version createMinVersion() {
+ if (minimumVersion == null)
+ minimumVersion = new OmniVersion(new Comparable[0], null, null, null);
+ return minimumVersion;
+ }
+
+ public static Version createMaxVersion() {
+ if (maximumVersion == null)
+ maximumVersion = new OmniVersion(new Comparable[0], VersionVector.MAX_VALUE, null, null);
+ return maximumVersion;
+ }
+
+ private OmniVersion(Comparable<?>[] array, Comparable<?> padValue, IVersionFormat format, String original) {
+ this.vector = array;
+ this.padValue = padValue;
+ this.format = format;
+ this.original = original;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+
+ if (!(o instanceof BasicVersion))
+ return false;
+
+ BasicVersion ov = (BasicVersion) o;
+ return VersionVector.equals(vector, padValue, ov.getVector(), ov.getPad());
+ }
+
+ public IVersionFormat getFormat() {
+ return format;
+ }
+
+ public int getMajor() {
+ return getIntElement(0);
+ }
+
+ public int getMicro() {
+ return getIntElement(2);
+ }
+
+ public int getMinor() {
+ return getIntElement(1);
+ }
+
+ public String getOriginal() {
+ return original;
+ }
+
+ public String getQualifier() {
+ if (vector.length == 3)
+ return VersionVector.MINS_VALUE;
+
+ if (vector.length != 4)
+ throw new UnsupportedOperationException();
+
+ Comparable<?> qualifier = vector[3];
+ if (qualifier == VersionVector.MAXS_VALUE)
+ return IVersionFormat.DEFAULT_MAX_STRING_TRANSLATION;
+ if (!(qualifier instanceof String))
+ throw new UnsupportedOperationException();
+ return (String) qualifier;
+ }
+
+ public int hashCode() {
+ return VersionVector.hashCode(vector, padValue);
+ }
+
+ /**
+ * Checks if this version is in compliance with the OSGi version spec.
+ * @return A flag indicating whether the version is OSGi compatible or not.
+ */
+ public boolean isOSGiCompatible() {
+ if (vector.length < 3 || vector.length > 4)
+ return (this == emptyVersion || this == MAX_VERSION);
+
+ if (getPad() != null)
+ return false;
+
+ for (int i = 0; i < 3; ++i) {
+ Object e = vector[i];
+ if (!(e instanceof Integer && ((Integer) e).intValue() >= 0))
+ return false;
+ }
+
+ if (vector.length == 3)
+ return true; // No qualifier. Still compatible
+ return OSGiVersion.isValidOSGiQualifier(vector[3]);
+ }
+
+ /**
+ * Appends the original for this version onto the <code>sb</code> StringBuffer
+ * if present.
+ * @param sb The buffer that will receive the raw string format
+ * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped
+ */
+ public void originalToString(StringBuffer sb, boolean rangeSafe) {
+ if (original != null) {
+ if (rangeSafe) {
+ // Escape all range delimiters while appending
+ String s = original;
+ int end = s.length();
+ for (int idx = 0; idx < end; ++idx) {
+ char c = s.charAt(idx);
+ if (c == '\\' || c == '[' || c == '(' || c == ']' || c == ')' || c == ',' || c <= ' ')
+ sb.append('\\');
+ sb.append(c);
+ }
+ } else
+ sb.append(original);
+ }
+ }
+
+ /**
+ * Appends the raw format for this version onto the <code>sb</code> StringBuffer.
+ * @param sb The buffer that will receive the raw string format
+ * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped
+ */
+ public void rawToString(StringBuffer sb, boolean rangeSafe) {
+ VersionVector.toString(sb, vector, padValue, rangeSafe);
+ }
+
+ /**
+ * Appends the string representation of this version onto the
+ * <code>sb</code> StringBuffer.
+ * @param sb The buffer that will receive the version string
+ */
+ public void toString(StringBuffer sb) {
+ if (this == emptyVersion)
+ sb.append("0.0.0"); //$NON-NLS-1$
+ else {
+ sb.append(RAW_PREFIX);
+ VersionVector.toString(sb, vector, padValue, false);
+ if (format != null || original != null) {
+ sb.append('/');
+ if (format != null)
+ format.toString(sb);
+ if (original != null) {
+ sb.append(':');
+ originalToString(sb, false);
+ }
+ }
+ }
+ }
+
+ private int getIntElement(int i) {
+ if (!(vector.length > i && vector[i] instanceof Integer))
+ throw new UnsupportedOperationException();
+ return ((Integer) vector[i]).intValue();
+ }
+
+ // Preserve singletons during deserialization
+ private Object readResolve() {
+ Version v = this;
+ if (equals(MAX_VERSION))
+ v = MAX_VERSION;
+ else if (equals(emptyVersion))
+ v = emptyVersion;
+ return v;
+ }
+
+ public Comparable<?> getPad() {
+ return padValue;
+ }
+
+ public Comparable<?> getSegment(int index) {
+ return vector[index];
+ }
+
+ public int getSegmentCount() {
+ return vector.length;
+ }
+
+ Comparable<?>[] getVector() {
+ return vector;
+ }
+
+ public int compareTo(Version v) {
+ BasicVersion ov = (BasicVersion) v;
+ return VersionVector.compare(vector, padValue, ov.getVector(), ov.getPad());
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ProvidedCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ProvidedCapability.java
index 1d8ed6dfa..06f8af8e1 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ProvidedCapability.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ProvidedCapability.java
@@ -12,7 +12,8 @@
package org.eclipse.equinox.internal.p2.metadata;
import org.eclipse.core.runtime.Assert;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.IProvidedCapability;
+import org.eclipse.equinox.p2.metadata.Version;
/**
* Describes a capability as exposed or required by an installable unit
@@ -59,17 +60,7 @@ public class ProvidedCapability implements IProvidedCapability {
return namespace.hashCode() * name.hashCode() * version.hashCode();
}
- /**
- * Returns whether this provided capability satisfies the given required capability.
- * @return <code>true</code> if this capability satisfies the given required
- * capability, and <code>false</code> otherwise.
- */
- public boolean satisfies(IRequiredCapability candidate) {
- return candidate.satisfiedBy(this);
- }
-
public String toString() {
return namespace + '/' + name + '/' + version;
}
-
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java
index 690c9e1ba..04d5b4ede 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequiredCapability.java
@@ -12,7 +12,9 @@
package org.eclipse.equinox.internal.p2.metadata;
import org.eclipse.core.runtime.Assert;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.expression.*;
+import org.osgi.framework.Filter;
/**
* A required capability represents some external constraint on an {@link IInstallableUnit}.
@@ -28,72 +30,124 @@ import org.eclipse.equinox.internal.provisional.p2.metadata.*;
* @see IInstallableUnit#NAMESPACE_IU_ID
*/
public class RequiredCapability implements IRequiredCapability {
- private static final String[] NO_SELECTORS = new String[0];
+ private final Filter filter;
+ private final boolean greedy;
+ private final IMatchExpression<IInstallableUnit> matchExpression;
+ private final int min;
+ private final int max;
- private String filter;
- private final boolean multiple;
- private final String name;//never null
- private final String namespace;//never null
- private boolean optional;
- private boolean greedy = true;
- private final VersionRange range;//never null
- private String[] selectors = NO_SELECTORS;//never null
+ private static final String MEMBER_NAME = "name"; //$NON-NLS-1$
+ private static final String MEMBER_NAMESPACE = "namespace"; //$NON-NLS-1$
+ private static final String MEMBER_VERSION = "version"; //$NON-NLS-1$
+ private static final String MEMBER_PROVIDED_CAPABILITIES = "providedCapabilities"; //$NON-NLS-1$
+
+ private static final IExpression allVersionsExpression;
+ private static final IExpression range_II_Expression;
+ private static final IExpression range_IN_Expression;
+ private static final IExpression range_NI_Expression;
+ private static final IExpression range_NN_Expression;
+ private static final IExpression strictVersionExpression;
+ private static final IExpression openEndedExpression;
+ private static final IExpression openEndedNonInclusiveExpression;
+
+ static {
+ IExpressionFactory factory = ExpressionUtil.getFactory();
+ IExpression xVar = factory.variable("x"); //$NON-NLS-1$
+ IExpression nameEqual = factory.equals(factory.member(xVar, MEMBER_NAME), factory.indexedParameter(0));
+ IExpression namespaceEqual = factory.equals(factory.member(xVar, MEMBER_NAMESPACE), factory.indexedParameter(1));
+
+ IExpression versionMember = factory.member(xVar, MEMBER_VERSION);
+
+ IExpression versionCmpLow = factory.indexedParameter(2);
+ IExpression versionEqual = factory.equals(versionMember, versionCmpLow);
+ IExpression versionGt = factory.greater(versionMember, versionCmpLow);
+ IExpression versionGtEqual = factory.greaterEqual(versionMember, versionCmpLow);
+
+ IExpression versionCmpHigh = factory.indexedParameter(3);
+ IExpression versionLt = factory.less(versionMember, versionCmpHigh);
+ IExpression versionLtEqual = factory.lessEqual(versionMember, versionCmpHigh);
+
+ IExpression pvMember = factory.member(factory.thisVariable(), MEMBER_PROVIDED_CAPABILITIES);
+ allVersionsExpression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual)));
+ strictVersionExpression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionEqual)));
+ openEndedExpression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionGtEqual)));
+ openEndedNonInclusiveExpression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionGt)));
+ range_II_Expression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionGtEqual, versionLtEqual)));
+ range_IN_Expression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionGtEqual, versionLt)));
+ range_NI_Expression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionGt, versionLtEqual)));
+ range_NN_Expression = factory.exists(pvMember, factory.lambda(xVar, factory.and(nameEqual, namespaceEqual, versionGt, versionLt)));
+ }
/**
* TODO replace booleans with int options flag.
*/
public RequiredCapability(String namespace, String name, VersionRange range, String filter, boolean optional, boolean multiple) {
- Assert.isNotNull(namespace);
- Assert.isNotNull(name);
- this.namespace = namespace;
- this.name = name;
- this.range = range == null ? VersionRange.emptyRange : range;
- this.optional = optional;
- this.filter = filter;
- this.multiple = multiple;
+ this(namespace, name, range, filter, optional, multiple, true);
}
public RequiredCapability(String namespace, String name, VersionRange range, String filter, boolean optional, boolean multiple, boolean greedy) {
- this(namespace, name, range, filter, optional, multiple);
+ this(namespace, name, range, filter == null ? (Filter) null : ExpressionUtil.parseLDAP(filter), optional ? 0 : 1, multiple ? 1 : Integer.MAX_VALUE, greedy);
+ }
+
+ public RequiredCapability(String namespace, String name, VersionRange range, Filter filter, int min, int max, boolean greedy) {
+ Assert.isNotNull(namespace);
+ Assert.isNotNull(name);
+ IExpressionFactory factory = ExpressionUtil.getFactory();
+ if (range == null || range.equals(VersionRange.emptyRange)) {
+ matchExpression = factory.matchExpression(allVersionsExpression, name, namespace);
+ } else {
+ if (range.getMinimum().equals(range.getMaximum())) {
+ // Explicit version appointed
+ matchExpression = factory.matchExpression(strictVersionExpression, name, namespace, range.getMinimum());
+ } else {
+ if (range.getMaximum().equals(Version.MAX_VERSION)) {
+ // Open ended
+ matchExpression = factory.matchExpression(range.getIncludeMinimum() ? openEndedExpression : openEndedNonInclusiveExpression, name, namespace, range.getMinimum());
+ } else {
+ matchExpression = factory.matchExpression(//
+ range.getIncludeMinimum() ? (range.getIncludeMaximum() ? range_II_Expression : range_IN_Expression) //
+ : (range.getIncludeMaximum() ? range_NI_Expression : range_NN_Expression), //
+ name, namespace, range.getMinimum(), range.getMaximum());
+ }
+ }
+ }
+ this.min = min;
+ this.max = max;
this.greedy = greedy;
+ this.filter = filter;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
- if (obj == null)
- return false;
- if (!(obj instanceof IRequiredCapability))
- return false;
- final IRequiredCapability other = (IRequiredCapability) obj;
- if (filter == null) {
- if (other.getFilter() != null)
+ if (obj instanceof RequiredCapability) {
+ RequiredCapability other = (RequiredCapability) obj;
+ if (filter == null) {
+ if (other.getFilter() != null)
+ return false;
+ } else if (!filter.equals(other.getFilter()))
return false;
- } else if (!filter.equals(other.getFilter()))
- return false;
- if (multiple != other.isMultiple())
- return false;
- if (!name.equals(other.getName()))
- return false;
- if (!namespace.equals(other.getNamespace()))
- return false;
- if (optional != other.isOptional())
- return false;
- if (!range.equals(other.getRange()))
- return false;
- return true;
- }
-
- public String getFilter() {
- return filter;
+ return min == other.min && max == other.max && greedy == other.greedy && matchExpression.equals(other.matchExpression);
+ }
+ if (obj instanceof IRequiredCapability) {
+ // Some other type of RequiredCapability
+ IRequiredCapability other = (IRequiredCapability) obj;
+ if (filter == null) {
+ if (other.getFilter() != null)
+ return false;
+ } else if (!filter.equals(other.getFilter()))
+ return false;
+ return min == other.getMin() && max == other.getMax() && greedy == other.isGreedy() && getName().equals(other.getName()) && getNamespace().equals(other.getNamespace()) && getRange().equals(other.getRange());
+ }
+ return false;
}
public String getName() {
- return name;
+ return (String) matchExpression.getParameters()[0];
}
public String getNamespace() {
- return namespace;
+ return (String) matchExpression.getParameters()[1];
}
/**
@@ -103,55 +157,17 @@ public class RequiredCapability implements IRequiredCapability {
* @return the range of versions that satisfy this required capability.
*/
public VersionRange getRange() {
- return range;
- }
-
- /**
- * Returns the properties to use for evaluating required capability filters
- * downstream from this capability. For example, if the selector "doc"
- * is provided, then a downstream InstallableUnit with a required capability
- * filtered with "doc=true" will be included.
- */
- public String[] getSelectors() {
- return selectors;
+ return extractRange(matchExpression);
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((filter == null) ? 0 : filter.hashCode());
- result = prime * result + (multiple ? 1231 : 1237);
- result = prime * result + name.hashCode();
- result = prime * result + namespace.hashCode();
- result = prime * result + (optional ? 1231 : 1237);
- result = prime * result + range.hashCode();
+ result = prime * result + matchExpression.hashCode();
return result;
}
- public boolean isMultiple() {
- return multiple;
- }
-
- public boolean isOptional() {
- return optional;
- }
-
- /**
- * TODO This object shouldn't be mutable since it makes equality unstable, and
- * introduces lifecycle issues (how are the changes persisted, etc)
- */
- public void setFilter(String filter) {
- this.filter = filter;
- }
-
- /**
- * TODO This object shouldn't be mutable since it makes equality unstable, and
- * introduces lifecycle issues (how are the changes persisted, etc)
- */
- public void setSelectors(String[] selectors) {
- this.selectors = selectors;
- }
-
public boolean isGreedy() {
return greedy;
}
@@ -173,6 +189,7 @@ public class RequiredCapability implements IRequiredCapability {
result.append(' ');
result.append(getName());
result.append(' ');
+ VersionRange range = getRange();
//for an exact version match, print a simpler expression
if (range.getMinimum().equals(range.getMaximum()))
result.append('[').append(range.getMinimum()).append(']');
@@ -181,15 +198,59 @@ public class RequiredCapability implements IRequiredCapability {
return result.toString();
}
- public boolean isNegation() {
- return false;
+ public int getMin() {
+ return min;
+ }
+
+ public int getMax() {
+ return max;
+ }
+
+ public IMatchExpression<IInstallableUnit> getMatches() {
+ return matchExpression;
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public boolean isMatch(IInstallableUnit candidate) {
+ return matchExpression.isMatch(candidate);
+ }
+
+ public static boolean isVersionStrict(IMatchExpression<IInstallableUnit> matchExpression) {
+ return ExpressionUtil.getOperand(matchExpression) == strictVersionExpression;
+ }
+
+ public static String extractName(IMatchExpression<IInstallableUnit> matchExpression) {
+ assertValid(matchExpression);
+ return (String) matchExpression.getParameters()[0];
+ }
+
+ public static String extractNamespace(IMatchExpression<IInstallableUnit> matchExpression) {
+ assertValid(matchExpression);
+ return (String) matchExpression.getParameters()[1];
+ }
+
+ public static VersionRange extractRange(IMatchExpression<IInstallableUnit> matchExpression) {
+ IExpression expr = assertValid(matchExpression);
+ Object[] params = matchExpression.getParameters();
+ if (params.length < 3)
+ return VersionRange.emptyRange;
+ Version v = (Version) params[2];
+ if (params.length < 4) {
+ if (expr == strictVersionExpression)
+ return new VersionRange(v, true, v, true);
+ return new VersionRange(v, expr == openEndedExpression, Version.MAX_VERSION, true);
+ }
+ Version h = (Version) params[3];
+ return new VersionRange(v, expr == range_II_Expression || expr == range_IN_Expression, h, expr == range_II_Expression || expr == range_NI_Expression);
}
- public boolean satisfiedBy(IProvidedCapability cap) {
- if (getName() == null || !getName().equals(cap.getName()))
- return false;
- if (getNamespace() == null || !getNamespace().equals(cap.getNamespace()))
- return false;
- return getRange().isIncluded(cap.getVersion());
+ private static IExpression assertValid(IMatchExpression<IInstallableUnit> matchExpression) {
+ IExpression expr = ExpressionUtil.getOperand(matchExpression);
+ if (!(expr == allVersionsExpression || expr == range_II_Expression || expr == range_IN_Expression || expr == range_NI_Expression || expr == range_NN_Expression || expr == strictVersionExpression || expr == openEndedExpression || expr == openEndedNonInclusiveExpression))
+ throw new IllegalArgumentException();
+ return expr;
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequirementChange.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequirementChange.java
index 49742867a..d67a1ca39 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequirementChange.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/RequirementChange.java
@@ -10,7 +10,7 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.IRequirementChange;
public class RequirementChange implements IRequirementChange {
private IRequiredCapability applyOn;
@@ -39,46 +39,7 @@ public class RequirementChange implements IRequirementChange {
if (toMatch.getRange().equals(applyOn.getRange()))
return true;
- return intersect(toMatch.getRange(), applyOn.getRange()) == null ? false : true;
- }
-
- private VersionRange intersect(VersionRange r1, VersionRange r2) {
- Version resultMin = null;
- boolean resultMinIncluded = false;
- Version resultMax = null;
- boolean resultMaxIncluded = false;
-
- int minCompare = r1.getMinimum().compareTo(r2.getMinimum());
- if (minCompare < 0) {
- resultMin = r2.getMinimum();
- resultMinIncluded = r2.getIncludeMinimum();
- } else if (minCompare > 0) {
- resultMin = r1.getMinimum();
- resultMinIncluded = r1.getIncludeMinimum();
- } else if (minCompare == 0) {
- resultMin = r1.getMinimum();
- resultMinIncluded = r1.getIncludeMinimum() && r2.getIncludeMinimum();
- }
-
- int maxCompare = r1.getMaximum().compareTo(r2.getMaximum());
- if (maxCompare > 0) {
- resultMax = r2.getMaximum();
- resultMaxIncluded = r2.getIncludeMaximum();
- } else if (maxCompare < 0) {
- resultMax = r1.getMaximum();
- resultMaxIncluded = r1.getIncludeMaximum();
- } else if (maxCompare == 0) {
- resultMax = r1.getMaximum();
- resultMaxIncluded = r1.getIncludeMaximum() && r2.getIncludeMaximum();
- }
-
- int resultRangeComparison = resultMin.compareTo(resultMax);
- if (resultRangeComparison < 0)
- return new VersionRange(resultMin, resultMinIncluded, resultMax, resultMaxIncluded);
- else if (resultRangeComparison == 0 && resultMinIncluded == resultMaxIncluded)
- return new VersionRange(resultMin, resultMinIncluded, resultMax, resultMaxIncluded);
- else
- return null;
+ return toMatch.getRange().intersect(applyOn.getRange()) != null;
}
public int hashCode() {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ResolvedInstallableUnit.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ResolvedInstallableUnit.java
index 0d4459aa7..580f5d5a4 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ResolvedInstallableUnit.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/ResolvedInstallableUnit.java
@@ -11,41 +11,48 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
+import org.eclipse.equinox.p2.metadata.Version;
+
import java.util.*;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
+import org.eclipse.equinox.p2.metadata.*;
+import org.osgi.framework.Filter;
public class ResolvedInstallableUnit implements IInstallableUnit {
- private static IInstallableUnit[] NO_IU = new IInstallableUnit[0];
+ private static IInstallableUnitFragment[] NO_IU = new IInstallableUnitFragment[0];
- private IInstallableUnit[] fragments = NO_IU;
- protected IInstallableUnit original;
+ private final IInstallableUnitFragment[] fragments;
+ protected final IInstallableUnit original;
public ResolvedInstallableUnit(IInstallableUnit resolved) {
- this.original = resolved;
+ this(resolved, null);
}
public ResolvedInstallableUnit(IInstallableUnit resolved, IInstallableUnitFragment[] fragments) {
this.original = resolved;
- this.fragments = fragments;
+ this.fragments = fragments == null ? NO_IU : fragments;
}
- public IInstallableUnitFragment[] getFragments() {
- ArrayList result = new ArrayList();
- if (fragments != null)
- result.addAll(Arrays.asList(fragments));
- for (int i = 0; i < result.size(); i++) {
- IInstallableUnit fragment = (IInstallableUnit) result.get(i);
+ public List<IInstallableUnitFragment> getFragments() {
+ int fcount = fragments.length;
+ if (fcount == 0)
+ return CollectionUtils.emptyList();
+
+ ArrayList<IInstallableUnitFragment> result = new ArrayList<IInstallableUnitFragment>(fcount);
+ result.addAll(Arrays.asList(fragments));
+ for (int i = 0; i < fcount; i++) {
+ IInstallableUnit fragment = fragments[i];
if (fragment.isResolved())
- result.addAll(Arrays.asList(fragment.getFragments()));
+ result.addAll(fragment.getFragments());
}
- return (IInstallableUnitFragment[]) result.toArray(new IInstallableUnitFragment[result.size()]);
+ return result;
}
- public IArtifactKey[] getArtifacts() {
+ public Collection<IArtifactKey> getArtifacts() {
return original.getArtifacts();
}
- public String getFilter() {
+ public Filter getFilter() {
return original.getFilter();
}
@@ -57,47 +64,56 @@ public class ResolvedInstallableUnit implements IInstallableUnit {
return original.getProperty(key);
}
- public Map getProperties() {
+ public Map<String, String> getProperties() {
return original.getProperties();
}
- public IProvidedCapability[] getProvidedCapabilities() {
- ArrayList result = new ArrayList();
- result.addAll(Arrays.asList(original.getProvidedCapabilities()));
- for (int i = 0; i < fragments.length; i++) {
- result.addAll(Arrays.asList(fragments[i].getProvidedCapabilities()));
- }
- return (IProvidedCapability[]) result.toArray(new IProvidedCapability[result.size()]);
+ public String getProperty(String key, String locale) {
+ return original.getProperty(key, locale);
}
- public IRequiredCapability[] getRequiredCapabilities() {
- ArrayList result = new ArrayList();
- result.addAll(Arrays.asList(original.getRequiredCapabilities()));
- for (int i = 0; i < fragments.length; i++) {
- result.addAll(Arrays.asList(fragments[i].getRequiredCapabilities()));
- }
- return (IRequiredCapability[]) result.toArray(new IRequiredCapability[result.size()]);
+ public Collection<IProvidedCapability> getProvidedCapabilities() {
+ Collection<IProvidedCapability> originalCapabilities = original.getProvidedCapabilities();
+ if (fragments.length == 0)
+ return originalCapabilities;
+
+ ArrayList<IProvidedCapability> result = new ArrayList<IProvidedCapability>(originalCapabilities);
+ for (int i = 0; i < fragments.length; i++)
+ result.addAll(fragments[i].getProvidedCapabilities());
+ return result;
}
- public IRequiredCapability[] getMetaRequiredCapabilities() {
- ArrayList result = new ArrayList();
- result.addAll(Arrays.asList(original.getMetaRequiredCapabilities()));
- for (int i = 0; i < fragments.length; i++) {
- result.addAll(Arrays.asList(fragments[i].getMetaRequiredCapabilities()));
- }
- return (IRequiredCapability[]) result.toArray(new IRequiredCapability[result.size()]);
+ public Collection<IRequirement> getRequiredCapabilities() {
+ Collection<IRequirement> originalCapabilities = original.getRequiredCapabilities();
+ if (fragments.length == 0)
+ return originalCapabilities;
+
+ ArrayList<IRequirement> result = new ArrayList<IRequirement>(originalCapabilities);
+ for (int i = 0; i < fragments.length; i++)
+ result.addAll(fragments[i].getRequiredCapabilities());
+ return result;
}
- public ITouchpointData[] getTouchpointData() {
- ArrayList result = new ArrayList();
- result.addAll(Arrays.asList(original.getTouchpointData()));
- for (int i = 0; i < fragments.length; i++) {
- ITouchpointData[] data = fragments[i].getTouchpointData();
- for (int j = 0; j < data.length; j++) {
- result.add(data[j]);
- }
- }
- return (ITouchpointData[]) result.toArray(new ITouchpointData[result.size()]);
+ public Collection<IRequirement> getMetaRequiredCapabilities() {
+ Collection<IRequirement> originalCapabilities = original.getMetaRequiredCapabilities();
+ if (fragments.length == 0)
+ return originalCapabilities;
+
+ ArrayList<IRequirement> result = new ArrayList<IRequirement>(originalCapabilities);
+ for (int i = 0; i < fragments.length; i++)
+ result.addAll(fragments[i].getMetaRequiredCapabilities());
+ return result;
+ }
+
+ public List<ITouchpointData> getTouchpointData() {
+ List<ITouchpointData> originalTouchpointData = original.getTouchpointData();
+ if (fragments.length == 0)
+ return originalTouchpointData;
+
+ ArrayList<ITouchpointData> result = new ArrayList<ITouchpointData>(originalTouchpointData);
+ for (int i = 0; i < fragments.length; i++)
+ result.addAll(fragments[i].getTouchpointData());
+ return result;
}
public ITouchpointType getTouchpointType() {
@@ -108,10 +124,6 @@ public class ResolvedInstallableUnit implements IInstallableUnit {
return original.getVersion();
}
- public boolean isFragment() {
- return original.isFragment();
- }
-
public boolean isSingleton() {
return original.isSingleton();
}
@@ -139,14 +151,11 @@ public class ResolvedInstallableUnit implements IInstallableUnit {
return original;
}
- public int compareTo(Object toCompareTo) {
- if (!(toCompareTo instanceof IInstallableUnit)) {
- return -1;
- }
- IInstallableUnit other = (IInstallableUnit) toCompareTo;
- if (getId().compareTo(other.getId()) == 0)
- return (getVersion().compareTo(other.getVersion()));
- return getId().compareTo(other.getId());
+ public int compareTo(IInstallableUnit other) {
+ int cmp = getId().compareTo(other.getId());
+ if (cmp == 0)
+ cmp = getVersion().compareTo(other.getVersion());
+ return cmp;
}
public boolean isResolved() {
@@ -161,20 +170,24 @@ public class ResolvedInstallableUnit implements IInstallableUnit {
return original.getUpdateDescriptor();
}
- public ILicense getLicense() {
- return original.getLicense();
+ public Collection<ILicense> getLicenses() {
+ return original.getLicenses();
+ }
+
+ public ILicense[] getLicenses(String locale) {
+ return original.getLicenses(locale);
}
public ICopyright getCopyright() {
return original.getCopyright();
}
- public boolean satisfies(IRequiredCapability candidate) {
- IProvidedCapability[] provides = getProvidedCapabilities();
- for (int i = 0; i < provides.length; i++)
- if (provides[i].satisfies(candidate))
- return true;
- return false;
+ public ICopyright getCopyright(String locale) {
+ return original.getCopyright(locale);
+ }
+
+ public boolean satisfies(IRequirement candidate) {
+ return candidate.isMatch(this);
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointData.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointData.java
index 9ea83d479..791363dd5 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointData.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointData.java
@@ -11,9 +11,12 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import java.util.*;
+import java.util.Collections;
+import java.util.Map;
import java.util.Map.Entry;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory;
+import org.eclipse.equinox.p2.metadata.ITouchpointData;
+import org.eclipse.equinox.p2.metadata.ITouchpointInstruction;
/**
* Touchpoint data instances contain the additional information needed by a touchpoint
@@ -31,7 +34,7 @@ public class TouchpointData implements ITouchpointData {
* of keys supported is up to the touchpoint that will process these
* instructions. This map is never null.
*/
- private Map instructions;
+ private Map<String, ITouchpointInstruction> instructions;
public int hashCode() {
return 31 * 1 + ((instructions == null) ? 0 : instructions.hashCode());
@@ -56,7 +59,7 @@ public class TouchpointData implements ITouchpointData {
/**
* Clients must use the factory method on {@link MetadataFactory}.
*/
- public TouchpointData(Map instructions) {
+ public TouchpointData(Map<String, ITouchpointInstruction> instructions) {
this.instructions = instructions;
}
@@ -64,7 +67,7 @@ public class TouchpointData implements ITouchpointData {
* Returns the touchpoint instruction corresponding to the given key.
*/
public ITouchpointInstruction getInstruction(String instructionKey) {
- return (ITouchpointInstruction) instructions.get(instructionKey);
+ return instructions.get(instructionKey);
}
/**
@@ -73,7 +76,7 @@ public class TouchpointData implements ITouchpointData {
*
* @return the touchpoint instructions
*/
- public Map getInstructions() {
+ public Map<String, ITouchpointInstruction> getInstructions() {
return Collections.unmodifiableMap(instructions);
}
@@ -82,8 +85,7 @@ public class TouchpointData implements ITouchpointData {
*/
public String toString() {
StringBuffer result = new StringBuffer();
- for (Iterator iterator = instructions.entrySet().iterator(); iterator.hasNext();) {
- Entry instruction = (Entry) iterator.next();
+ for (Entry<String, ITouchpointInstruction> instruction : instructions.entrySet()) {
result.append(instruction.getKey()).append(" -> ").append(instruction.getValue()).append('\n'); //$NON-NLS-1$
}
return result.toString();
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointInstruction.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointInstruction.java
index e59e9beea..5928639a1 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointInstruction.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointInstruction.java
@@ -11,10 +11,10 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import java.util.Iterator;
import java.util.Map;
-import org.eclipse.equinox.internal.provisional.p2.metadata.ITouchpointInstruction;
+import java.util.Map.Entry;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory;
+import org.eclipse.equinox.p2.metadata.ITouchpointInstruction;
/**
* A touchpoint instruction contains either a sequence of instruction statements
@@ -65,16 +65,18 @@ public class TouchpointInstruction implements ITouchpointInstruction {
* where the keys are parameter names, and the values are parameter values
* @return An encoded touchpoint instruction statement
*/
- public static String encodeAction(String actionName, Map parameters) {
+ public static String encodeAction(String actionName, Map<String, String> parameters) {
StringBuffer result = new StringBuffer(actionName);
result.append('(');
- for (Iterator it = parameters.entrySet().iterator(); it.hasNext();) {
- Map.Entry entry = (Map.Entry) it.next();
+ boolean first = true;
+ for (Entry<String, String> entry : parameters.entrySet()) {
+ if (first)
+ first = false;
+ else
+ result.append(',');
result.append(entry.getKey());
result.append(':');
- appendEncoded(result, (String) entry.getValue());
- if (it.hasNext())
- result.append(',');
+ appendEncoded(result, entry.getValue());
}
result.append(')').append(';');
return result.toString();
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointType.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointType.java
index 4056d7f09..86bb05b87 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointType.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TouchpointType.java
@@ -11,8 +11,9 @@
*******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.provisional.p2.metadata.ITouchpointType;
-import org.eclipse.equinox.internal.provisional.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.Version;
+
+import org.eclipse.equinox.p2.metadata.ITouchpointType;
/**
* Identifies a particular touchpoint. A touchpoint is identified by an id
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TranslationSupport.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TranslationSupport.java
new file mode 100644
index 000000000..e42289040
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/TranslationSupport.java
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ * EclipseSource - ongoing development
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata;
+
+import java.lang.ref.SoftReference;
+import java.util.*;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
+import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory;
+import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil;
+import org.eclipse.equinox.p2.metadata.expression.IExpression;
+import org.eclipse.equinox.p2.metadata.query.ExpressionQuery;
+import org.eclipse.equinox.p2.metadata.query.FragmentQuery;
+import org.eclipse.equinox.p2.query.*;
+import org.eclipse.osgi.service.localization.LocaleProvider;
+
+/**
+ * TranslationSupport provides string translations for properties of an
+ * IInstallableUnit. Clients can specify an {@link IQueryable} that should be used
+ * to obtain the translation fragment IU's, as well as the locale that
+ * should be used for translations.
+ *
+ * @since 2.0
+ */
+public class TranslationSupport {
+ // TODO: these constants should come from API, eg. IInstallableUnit or ???
+ static final Locale DEFAULT_LOCALE = new Locale("df", "LT"); //$NON-NLS-1$//$NON-NLS-2$
+ private static TranslationSupport instance;
+
+ static final String NAMESPACE_IU_LOCALIZATION = "org.eclipse.equinox.p2.localization"; //$NON-NLS-1$
+ private IQueryable<IInstallableUnit> fragmentSource;
+
+ private static IExpression capabilityMatch = ExpressionUtil.parse("providedCapabilities.exists(x | x.name == $0 && x.namespace == $1)"); //$NON-NLS-1$
+
+ // Cache the IU fragments that provide localizations for a given locale.
+ // Map<String,SoftReference<IQueryResult>>: locale => soft reference to a queryResult
+ private Map<String, SoftReference<IQueryResult<IInstallableUnit>>> localeCollectorCache = new HashMap<String, SoftReference<IQueryResult<IInstallableUnit>>>(2);
+
+ private LocaleProvider localeProvider;
+
+ public synchronized static TranslationSupport getInstance() {
+ if (instance == null)
+ instance = new TranslationSupport();
+ return instance;
+ }
+
+ /**
+ * Create an instance of TranslationSupport for the current locale.
+ * Unless otherwise specified, the currently running profile will serve
+ * as the source of the translation fragments.
+ *
+ * @since 2.0
+ */
+ public TranslationSupport() {
+ super();
+ }
+
+ /**
+ */
+ private List<String> buildLocaleVariants(String locale) {
+ ArrayList<String> result = new ArrayList<String>(4);
+ int lastSeparator;
+ while (true) {
+ result.add(locale);
+ lastSeparator = locale.lastIndexOf('_');
+ if (lastSeparator == -1)
+ break;
+ locale = locale.substring(0, lastSeparator);
+ }
+ // Add the default locale (most general)
+ result.add(DEFAULT_LOCALE.toString());
+ return result;
+ }
+
+ /**
+ * Cache the translated property value to optimize future retrieval of the same value.
+ * Currently we just cache on the installable unit object in memory. In future
+ * we should push support for localized property retrieval into IInstallableUnit
+ * so we aren't required to reach around the API here.
+ */
+ private String cacheResult(IInstallableUnit iu, String localizedKey, String localizedValue) {
+ if (iu instanceof InstallableUnit)
+ ((InstallableUnit) iu).setLocalizedProperty(localizedKey, localizedValue);
+ return localizedValue;
+ }
+
+ /**
+ * Return the copyright for the specified IInstallableUnit,
+ * localized for the receiver's locale.
+ *
+ * @param iu the IInstallableUnit in question
+ * @return the localized copyright defined by the IInstallableUnit
+ */
+ public ICopyright getCopyright(IInstallableUnit iu, String locale) {
+ if (locale == null)
+ locale = getCurrentLocale();
+ ICopyright copyright = iu.getCopyright();
+ String body = (copyright != null ? copyright.getBody() : null);
+ if (body == null || body.length() <= 1 || body.charAt(0) != '%')
+ return copyright;
+ final String actualKey = body.substring(1); // Strip off the %
+ body = getLocalizedIUProperty(iu, actualKey, locale);
+ return MetadataFactory.createCopyright(copyright.getLocation(), body);
+ }
+
+ private String getCurrentLocale() {
+ if (localeProvider != null)
+ return localeProvider.getLocale().toString();
+ return Locale.getDefault().toString();
+ }
+
+ /**
+ * Return the localized value for the specified IInstallableUnit
+ * property.
+ *
+ * @param iu the IInstallableUnit in question
+ * @param propertyKey the name of the property to be retrieved
+ * @param locale The locale to return the property for
+ * @return the localized property value, or <code>null</code> if no
+ * such property is defined.
+ */
+ public String getIUProperty(IInstallableUnit iu, String propertyKey, String locale) {
+ if (locale == null)
+ locale = getCurrentLocale();
+ String value = iu.getProperty(propertyKey);
+ if (value == null || value.length() <= 1 || value.charAt(0) != '%')
+ return value;
+ // else have a localizable property
+ final String actualKey = value.substring(1); // Strip off the %
+ return getLocalizedIUProperty(iu, actualKey, locale);
+ }
+
+ /**
+ * Return the localized value for the specified IInstallableUnit
+ * property using the default locale.
+ *
+ * @param iu the IInstallableUnit in question
+ * @param propertyKey the name of the property to be retrieved
+ * @return the localized property value, or <code>null</code> if no
+ * such property is defined.
+ */
+ public String getIUProperty(IInstallableUnit iu, String propertyKey) {
+ return getIUProperty(iu, propertyKey, null);
+ }
+
+ private ILicense getLicense(IInstallableUnit iu, ILicense license, String locale) {
+ String body = (license != null ? license.getBody() : null);
+ if (body == null || body.length() <= 1 || body.charAt(0) != '%')
+ return license;
+ final String actualKey = body.substring(1); // Strip off the %
+ body = getLocalizedIUProperty(iu, actualKey, locale);
+ return MetadataFactory.createLicense(license.getLocation(), body);
+ }
+
+ /**
+ * Return an array of licenses for the specified IInstallableUnit,
+ * localized for the receiver's locale.
+ *
+ * @param iu the IInstallableUnit in question
+ * @return the localized licenses defined by the IInstallableUnit
+ */
+ public ILicense[] getLicenses(IInstallableUnit iu, String locale) {
+ if (locale == null)
+ locale = getCurrentLocale();
+ Collection<ILicense> licenses = iu.getLicenses();
+ ILicense[] translatedLicenses = new ILicense[licenses.size()];
+ int i = 0;
+ for (ILicense iLicense : licenses) {
+ translatedLicenses[i++] = getLicense(iu, iLicense, locale);
+ }
+ return translatedLicenses;
+ }
+
+ /**
+ * Collects the installable unit fragments that contain locale data for the given locales.
+ */
+ private synchronized IQueryResult<IInstallableUnit> getLocalizationFragments(List<String> localeVariants, String locale) {
+ if (fragmentSource == null) {
+ LogHelper.log(new Status(IStatus.ERROR, MetadataActivator.PI_METADATA, "Profile registry unavailable. Default language will be used.", new RuntimeException())); //$NON-NLS-1$
+ return Collector.emptyCollector();
+ }
+
+ SoftReference<IQueryResult<IInstallableUnit>> queryResultReference = localeCollectorCache.get(locale);
+ if (queryResultReference != null) {
+ Collector<IInstallableUnit> cached = (Collector<IInstallableUnit>) queryResultReference.get();
+ if (cached != null)
+ return cached;
+ }
+
+ final List<String> locales = localeVariants;
+
+ @SuppressWarnings("unchecked")
+ IQuery<IInstallableUnit>[] localeQuery = new IQuery[locales.size()];
+ for (int j = 0; j < locales.size(); j++) {
+ localeQuery[j] = new ExpressionQuery<IInstallableUnit>(IInstallableUnit.class, capabilityMatch, locales.get(j), NAMESPACE_IU_LOCALIZATION);
+ }
+
+ IQuery<IInstallableUnit> iuQuery = new PipedQuery<IInstallableUnit>(new FragmentQuery(), CompoundQuery.createCompoundQuery(localeQuery, false));
+ IQueryResult<IInstallableUnit> collected = fragmentSource.query(iuQuery, null);
+ localeCollectorCache.put(locale, new SoftReference<IQueryResult<IInstallableUnit>>(collected));
+ return collected;
+ }
+
+ private String getLocalizedIUProperty(IInstallableUnit iu, String actualKey, String locale) {
+ String localizedKey = makeLocalizedKey(actualKey, locale);
+ String localizedValue = null;
+
+ //first check for a cached localized value
+ if (iu instanceof InstallableUnit)
+ localizedValue = ((InstallableUnit) iu).getLocalizedProperty(localizedKey);
+ //next check if the localized value is stored in the same IU (common case)
+ if (localizedValue == null)
+ localizedValue = iu.getProperty(localizedKey);
+ if (localizedValue != null)
+ return localizedValue;
+
+ final List<String> locales = buildLocaleVariants(locale);
+ final IInstallableUnit theUnit = iu;
+
+ IQueryResult<IInstallableUnit> localizationFragments = getLocalizationFragments(locales, locale);
+
+ IQuery<IInstallableUnit> hostLocalizationQuery = new MatchQuery<IInstallableUnit>() {
+ public boolean isMatch(IInstallableUnit object) {
+ boolean haveHost = false;
+ if (object instanceof IInstallableUnitFragment) {
+ IInstallableUnitFragment fragment = (IInstallableUnitFragment) object;
+ IRequirement[] hosts = fragment.getHost();
+ for (int i = 0; i < hosts.length; i++) {
+ if (theUnit.satisfies(hosts[i])) {
+ haveHost = true;
+ break;
+ }
+ }
+ }
+ return haveHost;
+ }
+ };
+
+ IQuery<IInstallableUnit> iuQuery = new PipedQuery<IInstallableUnit>(new FragmentQuery(), hostLocalizationQuery);
+ IQueryResult<IInstallableUnit> collected = iuQuery.perform(localizationFragments.iterator());
+ if (!collected.isEmpty()) {
+ String translation = null;
+ for (Iterator<IInstallableUnit> iter = collected.iterator(); iter.hasNext() && translation == null;) {
+ IInstallableUnit localizationIU = iter.next();
+ for (Iterator<String> jter = locales.iterator(); jter.hasNext();) {
+ String localeKey = makeLocalizedKey(actualKey, jter.next());
+ translation = localizationIU.getProperty(localeKey);
+ if (translation != null)
+ return cacheResult(iu, localizedKey, translation);
+ }
+ }
+ }
+
+ for (String nextLocale : locales) {
+ String localeKey = makeLocalizedKey(actualKey, nextLocale);
+ String nextValue = iu.getProperty(localeKey);
+ if (nextValue != null)
+ return cacheResult(iu, localizedKey, nextValue);
+ }
+
+ return cacheResult(iu, localizedKey, actualKey);
+ }
+
+ private String makeLocalizedKey(String actualKey, String localeImage) {
+ return localeImage + '.' + actualKey;
+ }
+
+ /**
+ * Set the locale that should be used when obtaining translations.
+ * @param provider the locale for which translations should be retrieved.
+ */
+ public void setLocaleProvider(LocaleProvider provider) {
+ this.localeProvider = provider;
+ }
+
+ /**
+ * Set the {@link IQueryable} that should be used to obtain translation fragment
+ * IUs. Returns the previous translation source.
+ *
+ * @param queryable an {@link IQueryable} that can supply the appropriate NLS
+ * translation fragments
+ */
+ public IQueryable<IInstallableUnit> setTranslationSource(IQueryable<IInstallableUnit> queryable) {
+ IQueryable<IInstallableUnit> previous = fragmentSource;
+ this.fragmentSource = queryable;
+ return previous;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/UpdateDescriptor.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/UpdateDescriptor.java
index d93a4500d..232f3abc6 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/UpdateDescriptor.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/UpdateDescriptor.java
@@ -8,7 +8,10 @@
******************************************************************************/
package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.VersionRange;
+
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IUpdateDescriptor;
public class UpdateDescriptor implements IUpdateDescriptor {
private String description;
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormat.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormat.java
new file mode 100644
index 000000000..3582ad18b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormat.java
@@ -0,0 +1,345 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata;
+
+import java.io.Serializable;
+import java.util.*;
+import org.eclipse.equinox.internal.p2.metadata.VersionFormatParser.Fragment;
+import org.eclipse.equinox.p2.metadata.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * <p>The VersionFormat represents the Omni Version Format in compiled form. It
+ * is also a parser for versions of that format.</p>
+ * <p>An instance of VersionFormat is immutable and thus thread safe. The parser
+ * does not maintain any state.</p>
+ *
+ * @Immutable
+ * @noextend This class is not intended to be subclassed by clients.
+ */
+public class VersionFormat implements IVersionFormat, Serializable {
+
+ /**
+ * The string representation of the Omni Version format used for parsing OSGi versions.
+ */
+ public static final String OSGI_FORMAT_STRING = "n[.n=0;[.n=0;[.S='';=[A-Za-z0-9_-];]]]"; //$NON-NLS-1$
+
+ /**
+ * The string representation of the Omni Version format used for parsing raw versions.
+ */
+ public static final String RAW_FORMAT_STRING = "r(.r)*p?"; //$NON-NLS-1$
+
+ private static final long serialVersionUID = -5689435955091405520L;
+
+ private static class StateInfo {
+ Fragment fragment;
+ int position;
+ int segmentCount;
+
+ StateInfo(int position, int segmentCount, Fragment fragment) {
+ this.fragment = fragment;
+ this.position = position;
+ this.segmentCount = segmentCount;
+ }
+ }
+
+ static class TreeInfo extends ArrayList<StateInfo> {
+ private static final long serialVersionUID = 4474591345244587260L;
+
+ private Comparable<?> padValue;
+ private int top;
+
+ TreeInfo(Fragment frag, int pos) {
+ add(new StateInfo(pos, 0, frag));
+ top = 0;
+ }
+
+ Comparable<?> getPadValue() {
+ return padValue;
+ }
+
+ int getPosition() {
+ return get(top).position;
+ }
+
+ void popState(List<Comparable<?>> segments, Fragment frag) {
+ int idx = top;
+ while (idx > 0) {
+ StateInfo si = get(idx);
+ if (si.fragment == frag) {
+ int nsegs = segments.size();
+ int segMax = si.segmentCount;
+ while (nsegs > segMax)
+ segments.remove(--nsegs);
+ top = idx - 1;
+ break;
+ }
+ }
+ }
+
+ void pushState(int segCount, Fragment fragment) {
+ int pos = get(top).position;
+ if (++top == size())
+ add(new StateInfo(pos, segCount, fragment));
+ else {
+ StateInfo si = get(top);
+ si.fragment = fragment;
+ si.position = pos;
+ si.segmentCount = segCount;
+ }
+ }
+
+ void setPadValue(Comparable<?> pad) {
+ padValue = pad;
+ }
+
+ void setPosition(int pos) {
+ get(top).position = pos;
+ }
+ }
+
+ private static final Map<String, VersionFormat> formatCache = Collections.synchronizedMap(new HashMap<String, VersionFormat>());
+
+ /**
+ * The predefined OSGi format that is used when parsing OSGi
+ * versions.
+ */
+ public static final VersionFormat OSGI_FORMAT;
+
+ /**
+ * The predefined OSGi format that is used when parsing raw
+ * versions.
+ */
+ public static final VersionFormat RAW_FORMAT;
+
+ static {
+ try {
+ VersionFormatParser parser = new VersionFormatParser();
+ OSGI_FORMAT = new VersionFormat(parser.compile(OSGI_FORMAT_STRING, 0, OSGI_FORMAT_STRING.length()));
+ formatCache.put(OSGI_FORMAT_STRING, OSGI_FORMAT);
+ RAW_FORMAT = new RawFormat(parser.compile(RAW_FORMAT_STRING, 0, RAW_FORMAT_STRING.length()));
+ formatCache.put(RAW_FORMAT_STRING, RAW_FORMAT);
+ } catch (VersionFormatException e) {
+ // If this happens, something is wrong with the actual
+ // implementation of the FormatCompiler.
+ //
+ throw new ExceptionInInitializerError(e);
+ }
+ }
+
+ /**
+ * Compile a version format string into a compiled format. This method is
+ * shorthand for:<pre>CompiledFormat.compile(format, 0, format.length())</pre>.
+ *
+ * @param format The format to compile.
+ * @return The compiled format
+ * @throws VersionFormatException If the format could not be compiled
+ */
+ public static IVersionFormat compile(String format) throws VersionFormatException {
+ return compile(format, 0, format.length());
+ }
+
+ /**
+ * Compile a version format string into a compiled format. The parsing starts
+ * at position start and ends at position end. The returned format is cached so
+ * subsequent calls to this method using the same format string will yield the
+ * same compiled format instance.
+ *
+ * @param format The format string to compile.
+ * @param start Start position in the format string
+ * @param end End position in the format string
+ * @return The compiled format
+ * @throws VersionFormatException If the format could not be compiled
+ */
+ public static VersionFormat compile(String format, int start, int end) throws VersionFormatException {
+ String fmtString = format.substring(start, end).intern();
+ synchronized (fmtString) {
+ VersionFormat fmt = formatCache.get(fmtString);
+ if (fmt == null) {
+ VersionFormatParser parser = new VersionFormatParser();
+ fmt = new VersionFormat(parser.compile(format, start, end));
+ formatCache.put(fmtString, fmt);
+ }
+ return fmt;
+ }
+ }
+
+ /**
+ * Parse a version string using the {@link #RAW_FORMAT} parser.
+ *
+ * @param version The version to parse.
+ * @param originalFormat The original format to assign to the created version. Can be <code>null</code>.
+ * @param original The original version string to assign to the created version. Can be <code>null</code>.
+ * @return A created version
+ * @throws IllegalArgumentException If the version string could not be parsed.
+ */
+ public static BasicVersion parseRaw(String version, IVersionFormat originalFormat, String original) {
+ Comparable<?>[] padReturn = new Comparable<?>[1];
+ Comparable<?>[] vector = RAW_FORMAT.parse(version, 0, version.length(), padReturn);
+ Comparable<?> pad = padReturn[0];
+ return (originalFormat == OSGI_FORMAT) ? OSGiVersion.fromVector(vector, pad) : OmniVersion.fromVector(vector, pad, originalFormat, original);
+ }
+
+ static void rawToString(StringBuffer sb, boolean forRange, Comparable<?> e) {
+ if (e instanceof String) {
+ writeQuotedString(sb, forRange, (String) e, '\'', 0, false);
+ } else if (e instanceof VersionVector) {
+ sb.append('<');
+ ((VersionVector) e).toString(sb, forRange);
+ sb.append('>');
+ } else
+ sb.append(e);
+ }
+
+ /**
+ * Write a string within quotes. If the string is found to contain the quote, an attempt is made
+ * to flip quote character (single quote becomes double quote and vice versa). A string that contains
+ * both will be written as several adjacent quoted strings so that each string is quoted with a
+ * quote character that it does not contain.
+ * @param sb The buffer that will receive the string
+ * @param rangeSafe Set to <code>true</code> if the resulting string will be used in a range string
+ * and hence need to escape the range delimiter characters
+ * @param s The string to be written
+ * @param quote The quote character to start with. Must be the single or double quote character.
+ * @param startPos The start position
+ * @param didFlip True if the call is recursive and thus, cannot switch quotes in the first string.
+ */
+ private static void writeQuotedString(StringBuffer sb, boolean rangeSafe, String s, char quote, int startPos, boolean didFlip) {
+ int quotePos = sb.length();
+ sb.append(quote);
+ boolean otherSeen = false;
+ int top = s.length();
+ for (int idx = startPos; idx < top; ++idx) {
+ char c = s.charAt(idx);
+ if (c == '\'' || c == '"') {
+ if (c == quote) {
+ char otherQuote = quote == '\'' ? '"' : '\'';
+ if (didFlip || otherSeen) {
+ // We can only flip once
+ sb.append(quote);
+ writeQuotedString(sb, rangeSafe, s, otherQuote, idx, true);
+ return;
+ }
+ quote = otherQuote;
+ sb.setCharAt(quotePos, quote);
+ didFlip = true;
+ } else
+ otherSeen = true;
+ }
+ if (rangeSafe && (c == '\\' || c == '[' || c == '(' || c == ']' || c == ')' || c == ',' || c <= ' '))
+ sb.append('\\');
+ sb.append(c);
+ }
+ sb.append(quote);
+ }
+
+ private String fmtString;
+
+ private final Fragment topFragment;
+
+ VersionFormat(Fragment topFragment) {
+ this.topFragment = topFragment;
+ }
+
+ TreeInfo createInfo(int start) {
+ return new TreeInfo(topFragment, start);
+ }
+
+ public boolean equals(Object o) {
+ return this == o || o instanceof VersionFormat && toString().equals(o.toString());
+ }
+
+ public int hashCode() {
+ return 11 * toString().hashCode();
+ }
+
+ public Version parse(String version) {
+ Comparable<?>[] padReturn = new Comparable<?>[1];
+ Comparable<?>[] vector = parse(version, 0, version.length(), padReturn);
+ Comparable<?> pad = padReturn[0];
+ return (this == OSGI_FORMAT) ? OSGiVersion.fromVector(vector, pad) : OmniVersion.fromVector(vector, pad, this, version);
+ }
+
+ Comparable<?>[] parse(String version, int start, int maxPos, Comparable<?>[] padReturn) {
+ if (start == maxPos)
+ throw new IllegalArgumentException(NLS.bind(Messages.format_0_unable_to_parse_empty_version, this, version.substring(start, maxPos)));
+ TreeInfo info = new TreeInfo(topFragment, start);
+ ArrayList<Comparable<?>> entries = new ArrayList<Comparable<?>>();
+ if (!(topFragment.parse(entries, version, maxPos, info) && info.getPosition() == maxPos))
+ throw new IllegalArgumentException(NLS.bind(Messages.format_0_unable_to_parse_1, this, version.substring(start, maxPos)));
+ padReturn[0] = VersionParser.removeRedundantTrail(entries, info.getPadValue());
+ return entries.toArray(new Comparable[entries.size()]);
+ }
+
+ // Preserve cache during deserialization
+ private Object readResolve() {
+ synchronized (formatCache) {
+ String string = toString();
+ VersionFormat fmt = formatCache.put(string, this);
+ if (fmt == null)
+ fmt = this;
+ else
+ // Put old format back
+ formatCache.put(string, fmt);
+ return fmt;
+ }
+ }
+
+ /**
+ * Returns the string representation of this compiled format
+ */
+ public synchronized String toString() {
+ if (fmtString == null) {
+ StringBuffer sb = new StringBuffer();
+ toString(sb);
+ }
+ return fmtString;
+ }
+
+ public synchronized void toString(StringBuffer sb) {
+ if (fmtString != null)
+ sb.append(fmtString);
+ else {
+ int start = sb.length();
+ sb.append("format"); //$NON-NLS-1$
+ if (topFragment.getPadValue() != null) {
+ sb.append('(');
+ topFragment.toString(sb);
+ sb.append(')');
+ } else
+ topFragment.toString(sb);
+ fmtString = sb.substring(start);
+ }
+ }
+}
+
+class RawFormat extends VersionFormat {
+ private static final long serialVersionUID = -6070590518921019745L;
+
+ RawFormat(Fragment topFragment) {
+ super(topFragment);
+ }
+
+ /**
+ * Parse but do not assign this format as the Version format nor the version
+ * string as the original.
+ */
+ public Version parse(String version, int start, int maxPos) {
+ Comparable<?>[] padReturn = new Comparable<?>[1];
+ Comparable<?>[] vector = parse(version, start, maxPos, padReturn);
+ return OmniVersion.fromVector(vector, padReturn[0], null, null);
+ }
+
+ // Preserve singleton when deserialized
+ private Object readResolve() {
+ return RAW_FORMAT;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormat.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormatParser.java
index 5de45b418..83170c9a7 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormat.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionFormatParser.java
@@ -8,25 +8,46 @@
* Contributors:
* Cloudsmith Inc. - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.p2.metadata.Messages;
+import org.eclipse.equinox.p2.metadata.VersionFormatException;
import java.io.Serializable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.equinox.internal.p2.metadata.VersionFormat.TreeInfo;
import org.eclipse.osgi.util.NLS;
/**
- * <p>The VersionFormat represents the Omni Version Format in compiled form. It
- * is also a parser for versions of that format.</p>
- * <p>An instance of VersionFormat is immutable and thus thread safe. The parser
- * does not maintain any state.</p>
- *
- * @Immutable
- * @noextend This class is not intended to be subclassed by clients.
+ * This is the Omni Version Format parser. It will parse a version format in string form
+ * into a group of {@link VersionFormatParser.Fragment} elements. That group, wrapped in a
+ * {@link VersionFormat}, becomes the parser for versions corresponding to the format.
+ *
+ * The class is not intended to included in a public API. Instead VersionFormats should
+ * be created using {@link VersionFormat#parse(String)}
+ *
*/
-public class VersionFormat implements Serializable {
- private static final long serialVersionUID = 6888925893926932754L;
+class VersionFormatParser {
+
+ static class Instructions {
+ char[] characters = null;
+ Comparable<?> defaultValue = null;
+ char oppositeTranslationChar = 0;
+ int oppositeTranslationRepeat = 0;
+ boolean ignore = false;
+ boolean inverted = false;
+ Comparable<?> padValue = null;
+ int rangeMax = Integer.MAX_VALUE;
+ int rangeMin = 0;
+ }
+
+ static final Qualifier EXACT_ONE_QUALIFIER = new Qualifier(1, 1);
+
+ static final Qualifier ONE_OR_MANY_QUALIFIER = new Qualifier(1, Integer.MAX_VALUE);
+
+ static final Qualifier ZERO_OR_MANY_QUALIFIER = new Qualifier(0, Integer.MAX_VALUE);
+
+ static final Qualifier ZERO_OR_ONE_QUALIFIER = new Qualifier(0, 1);
/**
* Represents one fragment of a format (i.e. auto, number, string, delimiter, etc.)
@@ -58,7 +79,7 @@ public class VersionFormat implements Serializable {
return sb.toString();
}
- Comparable getDefaultValue() {
+ Comparable<?> getDefaultValue() {
return null;
}
@@ -66,7 +87,7 @@ public class VersionFormat implements Serializable {
return this;
}
- Comparable getPadValue() {
+ Comparable<?> getPadValue() {
return null;
}
@@ -74,13 +95,13 @@ public class VersionFormat implements Serializable {
return qualifier;
}
- boolean parse(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parse(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
return qualifier.parse(new Fragment[] {this}, 0, segments, version, maxPos, info);
}
- abstract boolean parseOne(List segments, String version, int maxPos, TreeInfo info);
+ abstract boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info);
- void setDefaults(List segments) {
+ void setDefaults(List<Comparable<?>> segments) {
// No-op at this level
}
@@ -131,7 +152,7 @@ public class VersionFormat implements Serializable {
return min;
}
- boolean parse(Fragment[] fragments, int fragIdx, List segments, String version, int maxPos, TreeInfo info) {
+ boolean parse(Fragment[] fragments, int fragIdx, List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
Fragment fragment = fragments[fragIdx++];
int idx = 0;
@@ -157,9 +178,22 @@ public class VersionFormat implements Serializable {
for (;;) {
// Pad with default values unless the max is unbounded
//
- if (max != Integer.MAX_VALUE) {
- for (; idx < max; ++idx)
- fragment.setDefaults(segments);
+ if (idx < max) {
+ if (max != Integer.MAX_VALUE) {
+ for (; idx < max; ++idx)
+ fragment.setDefaults(segments);
+ }
+ } else {
+ if (fragment instanceof StringFragment) {
+ // Check for translations if we default to for MINS or MAXS
+ StringFragment stringFrag = (StringFragment) fragment;
+ Comparable<?> opposite = stringFrag.getOppositeDefaultValue();
+ if (opposite != null) {
+ idx = segments.size() - 1;
+ if (stringFrag.isOppositeTranslation(segments.get(idx)))
+ segments.set(idx, opposite);
+ }
+ }
}
if (fragIdx == fragments.length)
@@ -242,7 +276,7 @@ public class VersionFormat implements Serializable {
super(instr, qualifier);
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
maxPos = checkRange(pos, maxPos);
if (maxPos < 0)
@@ -266,7 +300,7 @@ public class VersionFormat implements Serializable {
return false;
if (!isIgnored())
- segments.add(Version.valueOf(value));
+ segments.add(VersionParser.valueOf(value));
info.setPosition(pos);
return true;
}
@@ -327,7 +361,7 @@ public class VersionFormat implements Serializable {
return true;
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
if (pos < maxPos && isMatch(version, pos)) {
// Just swallow, a delimiter does not contribute to the vector.
@@ -346,15 +380,86 @@ public class VersionFormat implements Serializable {
}
}
+ static void appendCharacterRange(StringBuffer sb, char[] range, boolean inverted) {
+ sb.append('=');
+ sb.append('[');
+ if (inverted)
+ sb.append('^');
+ int top = range.length;
+ for (int idx = 0; idx < top; ++idx) {
+ char b = range[idx];
+ if (b == '\\' || b == ']' || (b == '-' && idx + 1 < top))
+ sb.append('\\');
+
+ sb.append(b);
+ int ndx = idx + 1;
+ if (ndx + 2 < top) {
+ char c = b;
+ for (; ndx < top; ++ndx) {
+ char n = range[ndx];
+ if (c + 1 != n)
+ break;
+ c = n;
+ }
+ if (ndx <= idx + 3)
+ continue;
+
+ sb.append('-');
+ if (c == '\\' || c == ']' || (c == '-' && idx + 1 < top))
+ sb.append('\\');
+ sb.append(c);
+ idx = ndx - 1;
+ }
+ }
+ sb.append(']');
+ sb.append(';');
+ }
+
+ static Fragment createAutoFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
+ return new AutoFragment(instr, qualifier);
+ }
+
+ static Fragment createDelimiterFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
+ return new DelimiterFragment(instr, qualifier);
+ }
+
+ static Fragment createGroupFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, Fragment[] fragments, boolean array) {
+ return new GroupFragment(instr, qualifier, fragments, array);
+ }
+
+ static Fragment createLiteralFragment(Qualifier qualifier, String literal) {
+ return new LiteralFragment(qualifier, literal);
+ }
+
+ static Fragment createNumberFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, boolean signed) {
+ return new NumberFragment(instr, qualifier, signed);
+ }
+
+ static Fragment createPadFragment(Qualifier qualifier) {
+ return new PadFragment(qualifier);
+ }
+
+ static Fragment createQuotedFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
+ return new QuotedFragment(instr, qualifier);
+ }
+
+ static Fragment createRawFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
+ return new RawFragment(instr, qualifier);
+ }
+
+ static Fragment createStringFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, boolean unbound) {
+ return new StringFragment(instr, qualifier, unbound);
+ }
+
static boolean equalsAllowNull(Object a, Object b) {
return (a == null) ? (b == null) : (b != null && a.equals(b));
}
private static abstract class ElementFragment extends Fragment {
private static final long serialVersionUID = -6834591415456539713L;
- private final Comparable defaultValue;
+ private final Comparable<?> defaultValue;
private final boolean ignored;
- private final Comparable padValue;
+ private final Comparable<?> padValue;
ElementFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
super(qualifier);
@@ -369,11 +474,11 @@ public class VersionFormat implements Serializable {
}
}
- Comparable getDefaultValue() {
+ Comparable<?> getDefaultValue() {
return defaultValue;
}
- Comparable getPadValue() {
+ Comparable<?> getPadValue() {
return padValue;
}
@@ -381,8 +486,8 @@ public class VersionFormat implements Serializable {
return ignored;
}
- void setDefaults(List segments) {
- Object defaultVal = getDefaultValue();
+ void setDefaults(List<Comparable<?>> segments) {
+ Comparable<?> defaultVal = getDefaultValue();
if (defaultVal != null)
segments.add(defaultVal);
}
@@ -395,13 +500,13 @@ public class VersionFormat implements Serializable {
}
if (defaultValue != null) {
sb.append('=');
- VersionVector.rawToString(sb, false, defaultValue);
+ VersionFormat.rawToString(sb, false, defaultValue);
sb.append(';');
}
if (padValue != null) {
sb.append('=');
sb.append('p');
- VersionVector.rawToString(sb, false, padValue);
+ VersionFormat.rawToString(sb, false, padValue);
sb.append(';');
}
super.toString(sb);
@@ -427,26 +532,26 @@ public class VersionFormat implements Serializable {
return fragments[0].getFirstLeaf();
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
if (array) {
- ArrayList subSegs = new ArrayList();
+ ArrayList<Comparable<?>> subSegs = new ArrayList<Comparable<?>>();
boolean success = fragments[0].getQualifier().parse(fragments, 0, subSegs, version, maxPos, info);
if (!success || subSegs.isEmpty())
return false;
- Comparable padValue = info.getPadValue();
+ Comparable<?> padValue = info.getPadValue();
if (padValue != null)
info.setPadValue(null); // Prevent outer group from getting this.
else
padValue = getPadValue();
- VersionParser.removeRedundantTrail(segments, padValue);
- segments.add(new VersionVector((Comparable[]) subSegs.toArray(new Comparable[subSegs.size()]), padValue));
+ padValue = VersionParser.removeRedundantTrail(segments, padValue);
+ segments.add(new VersionVector(subSegs.toArray(new Comparable[subSegs.size()]), padValue));
return true;
}
if (fragments[0].getQualifier().parse(fragments, 0, segments, version, maxPos, info)) {
- Comparable padValue = getPadValue();
+ Comparable<?> padValue = getPadValue();
if (padValue != null)
info.setPadValue(padValue);
return true;
@@ -454,8 +559,8 @@ public class VersionFormat implements Serializable {
return false;
}
- void setDefaults(List segments) {
- Comparable dflt = getDefaultValue();
+ void setDefaults(List<Comparable<?>> segments) {
+ Comparable<?> dflt = getDefaultValue();
if (dflt != null) {
// A group default overrides any defaults within the
// group fragments
@@ -499,7 +604,7 @@ public class VersionFormat implements Serializable {
this.string = string;
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
int litLen = string.length();
if (pos + litLen > maxPos)
@@ -517,7 +622,7 @@ public class VersionFormat implements Serializable {
String str = string;
if (str.length() != 1) {
sb.append('\'');
- toStringEscaped(sb, str, "\'"); //$NON-NLS-1$
+ VersionFormatParser.toStringEscaped(sb, str, "\'"); //$NON-NLS-1$
sb.append('\'');
} else {
char c = str.charAt(0);
@@ -556,7 +661,7 @@ public class VersionFormat implements Serializable {
this.signed = signed;
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
maxPos = checkRange(pos, maxPos);
if (maxPos < 0)
@@ -611,7 +716,7 @@ public class VersionFormat implements Serializable {
return false;
if (!isIgnored())
- segments.add(Version.valueOf(value));
+ segments.add(VersionParser.valueOf(value));
info.setPosition(pos);
return true;
}
@@ -629,13 +734,13 @@ public class VersionFormat implements Serializable {
super(null, qualifier);
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
if (pos >= maxPos || version.charAt(pos) != 'p')
return false;
int[] position = new int[] {++pos};
- Comparable v = VersionParser.parseRawElement(version, position, maxPos);
+ Comparable<?> v = VersionParser.parseRawElement(version, position, maxPos);
if (v == null)
return false;
@@ -658,7 +763,7 @@ public class VersionFormat implements Serializable {
super(instr, qualifier);
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
if (pos >= maxPos)
return false;
@@ -809,9 +914,9 @@ public class VersionFormat implements Serializable {
super(processing, qualifier);
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int[] position = new int[] {info.getPosition()};
- Comparable v = VersionParser.parseRawElement(version, position, maxPos);
+ Comparable<?> v = VersionParser.parseRawElement(version, position, maxPos);
if (v == null)
return false;
@@ -830,13 +935,52 @@ public class VersionFormat implements Serializable {
private static class StringFragment extends RangeFragment {
private static final long serialVersionUID = -2265924553606430164L;
final boolean anyChar;
+ private final char oppositeTranslationChar;
+ private final int oppositeTranslationRepeat;
StringFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, boolean noLimit) {
super(instr, qualifier);
anyChar = noLimit;
+ char otc = 0;
+ int otr = 0;
+ if (instr != null) {
+ otc = instr.oppositeTranslationChar;
+ otr = instr.oppositeTranslationRepeat;
+ if (instr.defaultValue == VersionVector.MINS_VALUE) {
+ if (otc == 0)
+ otc = 'z';
+ if (otr == 0)
+ otr = 3;
+ } else if (instr.defaultValue == VersionVector.MAXS_VALUE) {
+ if (otc == 0)
+ otc = '-';
+ otr = 1;
+ }
+ }
+ oppositeTranslationChar = otc;
+ oppositeTranslationRepeat = otr;
+ }
+
+ Comparable<?> getOppositeDefaultValue() {
+ Comparable<?> dflt = getDefaultValue();
+ return dflt == VersionVector.MAXS_VALUE ? VersionVector.MINS_VALUE : (dflt == VersionVector.MINS_VALUE ? VersionVector.MAXS_VALUE : null);
+ }
+
+ public boolean isOppositeTranslation(Object val) {
+ if (val instanceof String) {
+ String str = (String) val;
+ int idx = oppositeTranslationRepeat;
+ if (str.length() == idx) {
+ while (--idx >= 0)
+ if (str.charAt(idx) != oppositeTranslationChar)
+ break;
+ return idx < 0;
+ }
+ }
+ return false;
}
- boolean parseOne(List segments, String version, int maxPos, TreeInfo info) {
+ boolean parseOne(List<Comparable<?>> segments, String version, int maxPos, TreeInfo info) {
int pos = info.getPosition();
maxPos = checkRange(pos, maxPos);
if (maxPos < 0)
@@ -888,360 +1032,517 @@ public class VersionFormat implements Serializable {
}
}
- private static class TreeInfo extends ArrayList {
- private static final long serialVersionUID = 4770093863009659750L;
+ private int current;
- private static class StateInfo {
- Fragment fragment;
- int segmentCount;
- int position;
+ private List<Fragment> currentList;
- StateInfo(int position, int segmentCount, Fragment fragment) {
- this.fragment = fragment;
- this.position = position;
- this.segmentCount = segmentCount;
- }
- }
+ private int eos;
- private Comparable padValue;
- private int top;
+ private String format;
- TreeInfo(Fragment frag, int pos) {
- add(new StateInfo(pos, 0, frag));
- top = 0;
- }
+ private int start;
- Comparable getPadValue() {
- return padValue;
- }
+ Fragment compile(String fmt, int pos, int maxPos) throws VersionFormatException {
+ format = fmt;
+ if (start >= maxPos)
+ throw new VersionFormatException(Messages.format_is_empty);
- int getPosition() {
- return ((StateInfo) get(top)).position;
- }
+ start = pos;
+ current = pos;
+ eos = maxPos;
+ currentList = new ArrayList<Fragment>();
+ while (current < eos)
+ parseFragment();
- void popState(List segments, Fragment frag) {
- int idx = top;
- while (idx > 0) {
- StateInfo si = (StateInfo) get(idx);
- if (si.fragment == frag) {
- int nsegs = segments.size();
- int segMax = si.segmentCount;
- while (nsegs > segMax)
- segments.remove(--nsegs);
- top = idx - 1;
+ Fragment topFrag;
+ switch (currentList.size()) {
+ case 0 :
+ throw new VersionFormatException(Messages.format_is_empty);
+ case 1 :
+ Fragment frag = currentList.get(0);
+ if (frag.isGroup()) {
+ topFrag = frag;
break;
}
- }
- }
-
- void pushState(int segCount, Fragment fragment) {
- int pos = ((StateInfo) get(top)).position;
- if (++top == size())
- add(new StateInfo(pos, segCount, fragment));
- else {
- StateInfo si = (StateInfo) get(top);
- si.fragment = fragment;
- si.position = pos;
- si.segmentCount = segCount;
- }
- }
-
- void setPadValue(Comparable pad) {
- padValue = pad;
- }
-
- void setPosition(int pos) {
- ((StateInfo) get(top)).position = pos;
+ // Fall through to default
+ default :
+ topFrag = createGroupFragment(null, EXACT_ONE_QUALIFIER, currentList.toArray(new Fragment[currentList.size()]), false);
}
+ currentList = null;
+ return topFrag;
}
- /**
- * The predefined OSGi format that is used when parsing OSGi
- * versions.
- */
- public static final VersionFormat OSGI_FORMAT;
-
- /**
- * The predefined OSGi format that is used when parsing raw
- * versions.
- */
- public static final VersionFormat RAW_FORMAT;
+ private void assertChar(char expected) throws VersionFormatException {
+ if (current >= eos)
+ throw formatException(NLS.bind(Messages.premature_end_of_format_expected_0, new String(new char[] {expected})));
- private static final Map formatCache = Collections.synchronizedMap(new HashMap());
+ char c = format.charAt(current);
+ if (c != expected)
+ throw formatException(c, new String(new char[] {expected}));
+ ++current;
+ }
- private static final String OSGI_FORMAT_STRING = "n[.n=0;[.n=0;[.S=[A-Za-z0-9_-];]]]"; //$NON-NLS-1$
+ private VersionFormatException formatException(char found, String expected) {
+ return formatException(new String(new char[] {found}), expected);
+ }
- private static final String RAW_FORMAT_STRING = "r(.r)*p?"; //$NON-NLS-1$
+ private VersionFormatException formatException(String message) {
+ return new VersionFormatException(NLS.bind(Messages.syntax_error_in_version_format_0_1_2, new Object[] {format.substring(start, eos), new Integer(current), message}));
+ }
- static {
- try {
- VersionFormatParser parser = new VersionFormatParser();
- OSGI_FORMAT = new VersionFormat(parser.compile(OSGI_FORMAT_STRING, 0, OSGI_FORMAT_STRING.length()));
- formatCache.put(OSGI_FORMAT_STRING, OSGI_FORMAT);
- RAW_FORMAT = new RawFormat(parser.compile(RAW_FORMAT_STRING, 0, RAW_FORMAT_STRING.length()));
- formatCache.put(RAW_FORMAT_STRING, RAW_FORMAT);
- } catch (FormatException e) {
- // If this happens, something is wrong with the actual
- // implementation of the FormatCompiler.
- //
- throw new ExceptionInInitializerError(e);
- }
+ private VersionFormatException formatException(String found, String expected) {
+ return new VersionFormatException(NLS.bind(Messages.syntax_error_in_version_format_0_1_found_2_expected_3, new Object[] {format.substring(start, eos), new Integer(current), found, expected}));
}
- /**
- * Compile a version format string into a compiled format. This method is
- * shorthand for:<pre>CompiledFormat.compile(format, 0, format.length())</pre>.
- *
- * @param format The format to compile.
- * @return The compiled format
- * @throws FormatException If the format could not be compiled
- */
- public static VersionFormat compile(String format) throws FormatException {
- return compile(format, 0, format.length());
+ private VersionFormatException illegalControlCharacter(char c) {
+ return formatException(NLS.bind(Messages.illegal_character_encountered_ascii_0, VersionParser.valueOf(c)));
}
- /**
- * Compile a version format string into a compiled format. The parsing starts
- * at position start and ends at position end. The returned format is cached so
- * subsequent calls to this method using the same format string will yield the
- * same compiled format instance.
- *
- * @param format The format string to compile.
- * @param start Start position in the format string
- * @param end End position in the format string
- * @return The compiled format
- * @throws FormatException If the format could not be compiled
- */
- public static VersionFormat compile(String format, int start, int end) throws FormatException {
- String fmtString = format.substring(start, end).intern();
- synchronized (fmtString) {
- VersionFormat fmt = (VersionFormat) formatCache.get(fmtString);
- if (fmt == null) {
- VersionFormatParser parser = new VersionFormatParser();
- fmt = new VersionFormat(parser.compile(format, start, end));
- formatCache.put(fmtString, fmt);
+ private String parseAndConsiderEscapeUntil(char endChar) throws VersionFormatException {
+ StringBuffer sb = new StringBuffer();
+ while (current < eos) {
+ char c = format.charAt(current++);
+ if (c == endChar)
+ break;
+
+ if (c < 32)
+ throw illegalControlCharacter(c);
+
+ if (c == '\\') {
+ if (current == eos)
+ throw formatException(Messages.EOS_after_escape);
+ c = format.charAt(current++);
+ if (c < 32)
+ throw illegalControlCharacter(c);
}
- return fmt;
+ sb.append(c);
}
+ return sb.toString();
}
- /**
- * Parse a version string using the {@link #RAW_FORMAT} parser.
- *
- * @param version The version to parse.
- * @param originalFormat The original format to assign to the created version. Can be <code>null</code>.
- * @param original The original version string to assign to the created version. Can be <code>null</code>.
- * @return A created version
- * @throws IllegalArgumentException If the version string could not be parsed.
- */
- static Version parseRaw(String version, VersionFormat originalFormat, String original) {
- Comparable[] padReturn = new Comparable[1];
- Comparable[] vector = RAW_FORMAT.parse(version, 0, version.length(), padReturn);
- return new Version(vector, padReturn[0], originalFormat, original);
+ private void parseAuto() throws VersionFormatException {
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.padValue != null)
+ throw formatException(Messages.auto_can_not_have_pad_value);
+ }
+ currentList.add(createAutoFragment(ep, parseQualifier()));
}
- static void appendCharacterRange(StringBuffer sb, char[] range, boolean inverted) {
- sb.append('=');
- sb.append('[');
- if (inverted)
- sb.append('^');
- int top = range.length;
- for (int idx = 0; idx < top; ++idx) {
- char b = range[idx];
- if (b == '\\' || b == ']' || (b == '-' && idx + 1 < top))
- sb.append('\\');
+ private void parseBracketGroup() throws VersionFormatException {
+ List<Fragment> saveList = currentList;
+ currentList = new ArrayList<Fragment>();
+ while (current < eos && format.charAt(current) != ']')
+ parseFragment();
- sb.append(b);
- int ndx = idx + 1;
- if (ndx + 2 < top) {
- char c = b;
- for (; ndx < top; ++ndx) {
- char n = range[ndx];
- if (c + 1 != n)
- break;
- c = n;
- }
- if (ndx <= idx + 3)
- continue;
+ if (current == eos)
+ throw formatException(NLS.bind(Messages.premature_end_of_format_expected_0, "]")); //$NON-NLS-1$
- sb.append('-');
- if (c == '\\' || c == ']' || (c == '-' && idx + 1 < top))
- sb.append('\\');
- sb.append(c);
- idx = ndx - 1;
+ ++current;
+ VersionFormatParser.Instructions ep = parseProcessing();
+ saveList.add(createGroupFragment(ep, ZERO_OR_ONE_QUALIFIER, currentList.toArray(new Fragment[currentList.size()]), false));
+ currentList = saveList;
+ }
+
+ private void parseCharacterGroup(VersionFormatParser.Instructions ep) throws VersionFormatException {
+ assertChar('[');
+
+ StringBuffer sb = new StringBuffer();
+ outer: for (; current < eos; ++current) {
+ char c = format.charAt(current);
+ switch (c) {
+ case '\\' :
+ if (current + 1 < eos) {
+ sb.append(format.charAt(++current));
+ continue;
+ }
+ throw formatException(Messages.premature_end_of_format);
+ case '^' :
+ if (sb.length() == 0)
+ ep.inverted = true;
+ else
+ sb.append(c);
+ continue;
+ case ']' :
+ break outer;
+ case '-' :
+ if (sb.length() > 0 && current + 1 < eos) {
+ char rangeEnd = format.charAt(++current);
+ if (rangeEnd == ']') {
+ // Use dash verbatim when last in range
+ sb.append(c);
+ break outer;
+ }
+
+ char rangeStart = sb.charAt(sb.length() - 1);
+ if (rangeEnd < rangeStart)
+ throw formatException(Messages.negative_character_range);
+ while (++rangeStart <= rangeEnd)
+ sb.append(rangeStart);
+ continue;
+ }
+ // Fall through to default
+ default :
+ if (c < 32)
+ throw illegalControlCharacter(c);
+ sb.append(c);
}
}
- sb.append(']');
- sb.append(';');
+ assertChar(']');
+ int top = sb.length();
+ char[] chars = new char[top];
+ sb.getChars(0, top, chars, 0);
+ ep.characters = chars;
}
- static Fragment createAutoFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
- return new AutoFragment(instr, qualifier);
+ private void parseDelimiter() throws VersionFormatException {
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.rangeMin != 0 || ep.rangeMax != Integer.MAX_VALUE)
+ throw formatException(Messages.delimiter_can_not_have_range);
+ if (ep.ignore)
+ throw formatException(Messages.delimiter_can_not_be_ignored);
+ if (ep.defaultValue != null)
+ throw formatException(Messages.delimiter_can_not_have_default_value);
+ if (ep.padValue != null)
+ throw formatException(Messages.delimiter_can_not_have_pad_value);
+ }
+ currentList.add(createDelimiterFragment(ep, parseQualifier()));
}
- static Fragment createDelimiterFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
- return new DelimiterFragment(instr, qualifier);
+ private void parseFragment() throws VersionFormatException {
+ if (current == eos)
+ throw formatException(Messages.premature_end_of_format);
+ char c = format.charAt(current++);
+ switch (c) {
+ case '(' :
+ parseGroup(false);
+ break;
+ case '<' :
+ parseGroup(true);
+ break;
+ case '[' :
+ parseBracketGroup();
+ break;
+ case 'a' :
+ parseAuto();
+ break;
+ case 'r' :
+ parseRaw();
+ break;
+ case 'n' :
+ parseNumber(false);
+ break;
+ case 'N' :
+ parseNumber(true);
+ break;
+ case 's' :
+ parseString(false);
+ break;
+ case 'S' :
+ parseString(true);
+ break;
+ case 'd' :
+ parseDelimiter();
+ break;
+ case 'q' :
+ parseQuotedString();
+ break;
+ case 'p' :
+ parsePad();
+ break;
+ default :
+ parseLiteral(c);
+ }
}
- static Fragment createGroupFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, Fragment[] fragments, boolean array) {
- return new GroupFragment(instr, qualifier, fragments, array);
- }
+ private void parseGroup(boolean array) throws VersionFormatException {
+ List<Fragment> saveList = currentList;
+ currentList = new ArrayList<Fragment>();
+ char expectedEnd = array ? '>' : ')';
+ while (current < eos && format.charAt(current) != expectedEnd)
+ parseFragment();
+ assertChar(expectedEnd);
+
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.characters != null)
+ throw formatException(Messages.array_can_not_have_character_group);
+ if (ep.rangeMax != Integer.MAX_VALUE && ep.padValue != null) {
+ throw formatException(Messages.cannot_combine_range_upper_bound_with_pad_value);
+ }
+ }
- static Fragment createLiteralFragment(Qualifier qualifier, String literal) {
- return new LiteralFragment(qualifier, literal);
+ if (currentList.isEmpty())
+ throw formatException(array ? Messages.array_can_not_be_empty : Messages.group_can_not_be_empty);
+ saveList.add(createGroupFragment(ep, parseQualifier(), currentList.toArray(new Fragment[currentList.size()]), array));
+ currentList = saveList;
}
- static Fragment createNumberFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, boolean signed) {
- return new NumberFragment(instr, qualifier, signed);
- }
+ private int parseIntegerLiteral() throws VersionFormatException {
+ if (current == eos)
+ throw formatException(NLS.bind(Messages.premature_end_of_format_expected_0, "<integer>")); //$NON-NLS-1$
- static Fragment createPadFragment(Qualifier qualifier) {
- return new PadFragment(qualifier);
- }
+ char c = format.charAt(current);
+ if (!VersionParser.isDigit(c))
+ throw formatException(c, "<integer>"); //$NON-NLS-1$
- static Fragment createQuotedFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
- return new QuotedFragment(instr, qualifier);
+ int value = c - '0';
+ while (++current < eos) {
+ c = format.charAt(current);
+ if (!VersionParser.isDigit(c))
+ break;
+ value *= 10;
+ value += (c - '0');
+ }
+ return value;
}
- static Fragment createRawFragment(VersionFormatParser.Instructions instr, Qualifier qualifier) {
- return new RawFragment(instr, qualifier);
+ private void parseLiteral(char c) throws VersionFormatException {
+ String value;
+ switch (c) {
+ case '\'' :
+ value = parseAndConsiderEscapeUntil(c);
+ break;
+ case ')' :
+ case ']' :
+ case '{' :
+ case '}' :
+ case '?' :
+ case '*' :
+ throw formatException(c, "<literal>"); //$NON-NLS-1$
+ default :
+ if (VersionParser.isLetterOrDigit(c))
+ throw formatException(c, "<literal>"); //$NON-NLS-1$
+
+ if (c < 32)
+ throw illegalControlCharacter(c);
+
+ if (c == '\\') {
+ if (current == eos)
+ throw formatException(Messages.EOS_after_escape);
+ c = format.charAt(current++);
+ if (c < 32)
+ throw illegalControlCharacter(c);
+ }
+ value = new String(new char[] {c});
+ }
+ currentList.add(createLiteralFragment(parseQualifier(), value));
}
- static Fragment createStringFragment(VersionFormatParser.Instructions instr, Qualifier qualifier, boolean unbound) {
- return new StringFragment(instr, qualifier, unbound);
+ private int[] parseMinMax() throws VersionFormatException {
+
+ int max = Integer.MAX_VALUE;
+ ++current;
+ int min = parseIntegerLiteral();
+ char c = format.charAt(current);
+ if (c == '}') {
+ max = min;
+ if (max == 0)
+ throw formatException(Messages.range_max_cannot_be_zero);
+ ++current;
+ } else if (c == ',' && current + 1 < eos) {
+ if (format.charAt(++current) != '}') {
+ max = parseIntegerLiteral();
+ if (max == 0)
+ throw formatException(Messages.range_max_cannot_be_zero);
+ if (max < min)
+ throw formatException(Messages.range_max_cannot_be_less_then_range_min);
+ }
+ assertChar('}');
+ } else
+ throw formatException(c, "},"); //$NON-NLS-1$
+ return new int[] {min, max};
}
- static void toStringEscaped(StringBuffer sb, String value, String escapes) {
- for (int idx = 0; idx < value.length(); ++idx) {
- char c = value.charAt(idx);
- if (c == '\\' || escapes.indexOf(c) >= 0)
- sb.append('\\');
- sb.append(c);
+ private void parseNumber(boolean signed) throws VersionFormatException {
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.padValue != null)
+ throw formatException(Messages.number_can_not_have_pad_value);
}
+ currentList.add(createNumberFragment(ep, parseQualifier(), signed));
}
- private final Fragment topFragment;
+ private void parsePad() throws VersionFormatException {
+ currentList.add(createPadFragment(parseQualifier()));
+ }
- private String fmtString;
+ private VersionFormatParser.Instructions parseProcessing() throws VersionFormatException {
+ if (current >= eos)
+ return null;
- VersionFormat(Fragment topFragment) {
- this.topFragment = topFragment;
- }
+ char c = format.charAt(current);
+ if (c != '=')
+ return null;
- public boolean equals(Object o) {
- return this == o || o instanceof VersionFormat && toString().equals(o.toString());
+ VersionFormatParser.Instructions ep = new VersionFormatParser.Instructions();
+ do {
+ current++;
+ parseProcessingInstruction(ep);
+ } while (current < eos && format.charAt(current) == '=');
+ return ep;
}
- public int hashCode() {
- return 11 * toString().hashCode();
- }
+ private void parseProcessingInstruction(VersionFormatParser.Instructions processing) throws VersionFormatException {
+ if (current == eos)
+ throw formatException(Messages.premature_end_of_format);
- /**
- * Parse the given version string.
- * @param version The version string to parse.
- * @return A created version.
- * @throws IllegalArgumentException If the version string could not be parsed.
- */
- public Version parse(String version) {
- return parse(version, 0, version.length());
+ char c = format.charAt(current);
+ if (c == 'p') {
+ // =pad(<raw-element>);
+ //
+ if (processing.padValue != null)
+ throw formatException(Messages.pad_defined_more_then_once);
+ if (processing.ignore)
+ throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
+ ++current;
+ processing.padValue = parseRawElement();
+ } else if (c == '!') {
+ // =ignore;
+ //
+ if (processing.ignore)
+ throw formatException(Messages.ignore_defined_more_then_once);
+ if (processing.padValue != null || processing.characters != null || processing.rangeMin != 0 || processing.rangeMax != Integer.MAX_VALUE || processing.defaultValue != null)
+ throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
+ ++current;
+ processing.ignore = true;
+ } else if (c == '[') {
+ // =[<character group];
+ //
+ if (processing.characters != null)
+ throw formatException(Messages.character_group_defined_more_then_once);
+ if (processing.ignore)
+ throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
+ parseCharacterGroup(processing);
+ } else if (c == '{') {
+ // ={min,max};
+ //
+ if (processing.rangeMin != 0 || processing.rangeMax != Integer.MAX_VALUE)
+ throw formatException(Messages.range_defined_more_then_once);
+ if (processing.ignore)
+ throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
+ int[] minMax = parseMinMax();
+ processing.rangeMin = minMax[0];
+ processing.rangeMax = minMax[1];
+ } else {
+ // =<raw-element>;
+ if (processing.defaultValue != null)
+ throw formatException(Messages.default_defined_more_then_once);
+ if (processing.ignore)
+ throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
+ Comparable<?> dflt = parseRawElement();
+ processing.defaultValue = dflt;
+ if (current < eos && format.charAt(current) == '{') {
+ // =m{<translated min char>}
+ // =''{<translated max char>,<max char repeat>}
+ if (++current == eos)
+ throw formatException(Messages.premature_end_of_format);
+ processing.oppositeTranslationChar = format.charAt(current++);
+ if (current == eos)
+ throw formatException(Messages.premature_end_of_format);
+
+ if (dflt == VersionVector.MINS_VALUE) {
+ processing.oppositeTranslationRepeat = 3;
+ if (format.charAt(current) == ',') {
+ ++current;
+ processing.oppositeTranslationRepeat = parseIntegerLiteral();
+ }
+ } else if (dflt != VersionVector.MAXS_VALUE) {
+ current -= 2;
+ throw formatException(Messages.only_max_and_empty_string_defaults_can_have_translations);
+ }
+ assertChar('}');
+ }
+ }
+ assertChar(';');
}
- /**
- * Parse the given version string.
- * @param version The version string to parse.
- * @param start Start position in the version string
- * @return A created version.
- * @throws IllegalArgumentException If the version string could not be parsed.
- */
- public Version parse(String version, int start, int maxPos) {
- Comparable[] padReturn = new Comparable[1];
- Comparable[] vector = parse(version, start, maxPos, padReturn);
- return new Version(vector, padReturn[0], this, version.substring(start, maxPos));
- }
+ private Qualifier parseQualifier() throws VersionFormatException {
+ if (current >= eos)
+ return EXACT_ONE_QUALIFIER;
- /**
- * Returns the string representation of this compiled format
- */
- public synchronized String toString() {
- if (fmtString == null) {
- StringBuffer sb = new StringBuffer();
- toString(sb);
+ char c = format.charAt(current);
+ if (c == '?') {
+ ++current;
+ return ZERO_OR_ONE_QUALIFIER;
}
- return fmtString;
- }
- /**
- * Appends the string representation of this compiled format to
- * the given StringBuffer.
- * @param sb The buffer that will receive the string representation
- */
- public synchronized void toString(StringBuffer sb) {
- if (fmtString != null)
- sb.append(fmtString);
- else {
- int start = sb.length();
- sb.append("format"); //$NON-NLS-1$
- if (topFragment.getPadValue() != null) {
- sb.append('(');
- topFragment.toString(sb);
- sb.append(')');
- } else
- topFragment.toString(sb);
- fmtString = sb.substring(start);
+ if (c == '*') {
+ ++current;
+ return ZERO_OR_MANY_QUALIFIER;
}
- }
- TreeInfo createInfo(int start) {
- return new TreeInfo(topFragment, start);
- }
+ if (c == '+') {
+ ++current;
+ return ONE_OR_MANY_QUALIFIER;
+ }
+
+ if (c != '{')
+ return EXACT_ONE_QUALIFIER;
- Comparable[] parse(String version, int start, int maxPos, Comparable[] padReturn) {
- ArrayList entries = new ArrayList();
- if (start == maxPos)
- throw new IllegalArgumentException(NLS.bind(Messages.format_0_unable_to_parse_empty_version, this, version.substring(start, maxPos)));
- TreeInfo info = new TreeInfo(topFragment, start);
- if (!(topFragment.parse(entries, version, maxPos, info) && info.getPosition() == maxPos))
- throw new IllegalArgumentException(NLS.bind(Messages.format_0_unable_to_parse_1, this, version.substring(start, maxPos)));
- Comparable pad = info.getPadValue();
- VersionParser.removeRedundantTrail(entries, pad);
- padReturn[0] = pad;
- return (Comparable[]) entries.toArray(new Comparable[entries.size()]);
+ int[] minMax = parseMinMax();
+ int min = minMax[0];
+ int max = minMax[1];
+
+ // Use singletons for commonly used ranges
+ //
+ if (min == 0) {
+ if (max == 1)
+ return ZERO_OR_ONE_QUALIFIER;
+ if (max == Integer.MAX_VALUE)
+ return ZERO_OR_MANY_QUALIFIER;
+ } else if (min == 1) {
+ if (max == 1)
+ return EXACT_ONE_QUALIFIER;
+ if (max == Integer.MAX_VALUE)
+ return ONE_OR_MANY_QUALIFIER;
+ }
+ return new Qualifier(min, max);
}
- // Preserve cache during deserialization
- private Object readResolve() {
- synchronized (formatCache) {
- String string = toString();
- VersionFormat fmt = (VersionFormat) formatCache.put(string, this);
- if (fmt == null)
- fmt = this;
- else
- // Put old format back
- formatCache.put(string, fmt);
- return fmt;
+ private void parseQuotedString() throws VersionFormatException {
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.padValue != null)
+ throw formatException(Messages.string_can_not_have_pad_value);
}
+ currentList.add(createQuotedFragment(ep, parseQualifier()));
}
-}
-class RawFormat extends VersionFormat {
- private static final long serialVersionUID = 8851695938450999819L;
+ private void parseRaw() throws VersionFormatException {
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.padValue != null)
+ throw formatException(Messages.raw_element_can_not_have_pad_value);
+ }
+ currentList.add(createRawFragment(ep, parseQualifier()));
+ }
- RawFormat(Fragment topFragment) {
- super(topFragment);
+ private Comparable<?> parseRawElement() throws VersionFormatException {
+ int[] position = new int[] {current};
+ Comparable<?> v = VersionParser.parseRawElement(format, position, eos);
+ if (v == null)
+ throw new VersionFormatException(NLS.bind(Messages.raw_element_expected_0, format));
+ current = position[0];
+ return v;
}
- /**
- * Parse but do not assign this format as the Version format nor the version
- * string as the original.
- */
- public Version parse(String version, int start, int maxPos) {
- Comparable[] padReturn = new Comparable[1];
- Comparable[] vector = parse(version, start, maxPos, padReturn);
- return new Version(vector, padReturn[0], null, null);
+ private void parseString(boolean unlimited) throws VersionFormatException {
+ VersionFormatParser.Instructions ep = parseProcessing();
+ if (ep != null) {
+ if (ep.padValue != null)
+ throw formatException(Messages.string_can_not_have_pad_value);
+ }
+ currentList.add(createStringFragment(ep, parseQualifier(), unlimited));
}
- // Preserve singleton when deserialized
- private Object readResolve() {
- return RAW_FORMAT;
+ static void toStringEscaped(StringBuffer sb, String value, String escapes) {
+ for (int idx = 0; idx < value.length(); ++idx) {
+ char c = value.charAt(idx);
+ if (c == '\\' || escapes.indexOf(c) >= 0)
+ sb.append('\\');
+ sb.append(c);
+ }
}
} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionParser.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionParser.java
index 138d895b8..0ef47d9bc 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionParser.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionParser.java
@@ -8,9 +8,10 @@
* Contributors:
* Cloudsmith Inc. - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.internal.p2.metadata;
-import org.eclipse.equinox.internal.p2.metadata.Messages;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.VersionFormatException;
import java.util.ArrayList;
import java.util.List;
@@ -18,21 +19,49 @@ import org.eclipse.osgi.util.NLS;
/**
* The Omni Version parser. Not intended for public API. Instead use
- * {@link Version#Version(String)} or {@link Version#parseVersion(String)}.
+ * {@link Version#create(String)} or {@link Version#parseVersion(String)}.
*
* The class also contains some general purpose parser support methods
*
* @noextend This class is not intended to be subclassed by clients.
*/
-abstract class VersionParser {
- static void removeRedundantTrail(List segments, Comparable padValue) {
- Comparable redundantTrail = padValue == null ? VersionVector.MIN_VALUE : padValue;
+public abstract class VersionParser {
+ public static final Integer ZERO_INT = new Integer(0);
+
+ public static final Integer MAX_INT_OBJ = new Integer(Integer.MAX_VALUE);
+
+ private static final Integer cache[] = new Integer[100];
+
+ static {
+ cache[0] = ZERO_INT;
+ for (int i = 1; i < cache.length; i++)
+ cache[i] = new Integer(i);
+ }
+
+ public static Integer valueOf(int i) {
+ try {
+ return cache[i];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return (i == Integer.MAX_VALUE) ? MAX_INT_OBJ : new Integer(i);
+ }
+ }
+
+ static Comparable<?> removeRedundantTrail(List<Comparable<?>> segments, Comparable<?> padValue) {
+ Comparable<?> redundantTrail;
+ if (padValue == null)
+ redundantTrail = VersionVector.MIN_VALUE;
+ else {
+ redundantTrail = padValue;
+ if (padValue == VersionVector.MIN_VALUE)
+ padValue = null;
+ }
+
int idx = segments.size();
while (--idx >= 0 && segments.get(idx).equals(redundantTrail))
segments.remove(idx);
- }
- static final String RAW_PREFIX = "raw:"; //$NON-NLS-1$
+ return padValue;
+ }
private VersionParser() {
// Prevent class from being instantiated
@@ -45,34 +74,31 @@ abstract class VersionParser {
* @param version The string to be parsed
* @param start Start position in the <code>version</code> string
* @param maxPos End position in the <code>version</code> string
- * @param receiver The version to be filled in
- * @returns <code>true</code> if a version indeed was parsed or <code>false</code> if the string
+ * @returns a version if one indeed was parsed or <code>null</code> if the string
* contained only whitespace.
* @throws IllegalArgumentException if the version is malformed
*/
- static boolean parseInto(String version, int start, int maxPos, Version receiver) throws IllegalArgumentException {
+ public static Version parse(String version, int start, int maxPos) throws IllegalArgumentException {
// trim leading and trailing whitespace
int pos = skipWhite(version, start);
maxPos = skipTrailingWhite(version, start, maxPos);
if (pos == maxPos)
- return false;
+ return null;
- Comparable[] padReturn = new Comparable[1];
- Comparable[] vector = null;
- Comparable pad = null;
+ Comparable<?>[] padReturn = new Comparable[1];
+ Comparable<?>[] vector = null;
+ Comparable<?> pad = null;
VersionFormat fmt = null;
char c = version.charAt(pos);
if (isDigit(c)) {
- fmt = VersionFormat.OSGI_FORMAT;
- vector = fmt.parse(version, pos, maxPos, padReturn);
- receiver.init(vector, padReturn[0], fmt, version);
- return true;
+ vector = VersionFormat.OSGI_FORMAT.parse(version, pos, maxPos, padReturn);
+ return OSGiVersion.fromVector(vector, padReturn[0]);
}
if (!isLetter(c))
throw new IllegalArgumentException();
- if (version.startsWith(RAW_PREFIX, pos)) {
+ if (version.startsWith(Version.RAW_PREFIX, pos)) {
VersionFormat rawFmt = VersionFormat.RAW_FORMAT;
pos += 4;
@@ -107,12 +133,10 @@ abstract class VersionParser {
vector = rawFmt.parse(version, pos, end, padReturn);
pad = padReturn[0];
pos = end;
- if (pos == maxPos) {
+ if (pos == maxPos)
// This was a pure raw version
//
- receiver.init(vector, pad, null, null);
- return true;
- }
+ return OmniVersion.fromVector(vector, pad, null, null);
if (version.charAt(pos) != '/')
throw new IllegalArgumentException(NLS.bind(Messages.expected_slash_after_raw_vector_0, version.substring(start, maxPos)));
@@ -132,7 +156,7 @@ abstract class VersionParser {
int end = findEndOfFormat(version, pos, maxPos);
fmt = VersionFormat.compile(version, pos, end);
pos = end + 1;
- } catch (FormatException e) {
+ } catch (VersionFormatException e) {
throw new IllegalArgumentException(e.getMessage());
}
if (pos == maxPos) {
@@ -140,8 +164,7 @@ abstract class VersionParser {
//
if (vector == null)
throw new IllegalArgumentException(NLS.bind(Messages.only_format_specified_0, version.substring(start, maxPos)));
- receiver.init(vector, pad, fmt, null);
- return true;
+ return fmt == VersionFormat.OSGI_FORMAT ? OSGiVersion.fromVector(vector, pad) : OmniVersion.fromVector(vector, pad, fmt, null);
}
}
@@ -161,15 +184,14 @@ abstract class VersionParser {
vector = fmt.parse(version, pos, maxPos, padReturn);
pad = padReturn[0];
}
- receiver.init(vector, pad, fmt, version.substring(pos));
- return true;
+ return fmt == VersionFormat.OSGI_FORMAT ? OSGiVersion.fromVector(vector, pad) : OmniVersion.fromVector(vector, pad, fmt, version.substring(pos));
}
static boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
- static boolean isLetter(char c) {
+ public static boolean isLetter(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
@@ -177,7 +199,7 @@ abstract class VersionParser {
return isDigit(c) || isLetter(c);
}
- static int findEndOfFormat(String string, int pos, int maxPos) {
+ public static int findEndOfFormat(String string, int pos, int maxPos) {
int end = -1;
int depth = 1;
for (int idx = pos; idx < maxPos; ++idx) {
@@ -216,14 +238,14 @@ abstract class VersionParser {
return end;
}
- static Comparable parseRawElement(String value, int[] position, int maxPos) {
+ static Comparable<?> parseRawElement(String value, int[] position, int maxPos) {
int current = position[0];
if (current >= maxPos)
return null;
boolean negate = false;
char c = value.charAt(current);
- Comparable v;
+ Comparable<?> v;
switch (c) {
case '\'' :
case '"' : {
@@ -247,7 +269,7 @@ abstract class VersionParser {
if (c != '\'' && c != '"')
break;
}
- v = sb.toString();
+ v = sb.length() == 0 ? VersionVector.MINS_VALUE : sb.toString();
break;
}
case '<' : {
@@ -289,7 +311,7 @@ abstract class VersionParser {
int val = Integer.parseInt(value.substring(start, current));
if (negate)
val = -val;
- v = Version.valueOf(val);
+ v = valueOf(val);
break;
}
return null;
@@ -299,7 +321,7 @@ abstract class VersionParser {
return v;
}
- private static Comparable parseRawVector(String value, int[] position, int maxPos) {
+ private static Comparable<?> parseRawVector(String value, int[] position, int maxPos) {
int pos = position[0];
if (pos >= maxPos)
return null;
@@ -308,7 +330,7 @@ abstract class VersionParser {
if (c == '>')
return null;
- ArrayList rawList = new ArrayList();
+ ArrayList<Comparable<?>> rawList = new ArrayList<Comparable<?>>();
boolean padMarkerSeen = (c == 'p');
if (padMarkerSeen) {
if (++pos >= maxPos)
@@ -316,9 +338,9 @@ abstract class VersionParser {
position[0] = pos;
}
- Comparable pad = null;
+ Comparable<?> pad = null;
for (;;) {
- Comparable elem = parseRawElement(value, position, maxPos);
+ Comparable<?> elem = parseRawElement(value, position, maxPos);
if (elem == null)
return null;
@@ -347,18 +369,18 @@ abstract class VersionParser {
if (c != '.')
return null;
}
- removeRedundantTrail(rawList, pad);
- return new VersionVector((Comparable[]) rawList.toArray(new Comparable[rawList.size()]), pad);
+ pad = removeRedundantTrail(rawList, pad);
+ return new VersionVector(rawList.toArray(new Comparable[rawList.size()]), pad);
}
- static int skipWhite(String string, int pos) {
+ public static int skipWhite(String string, int pos) {
int top = string.length();
while (pos < top && string.charAt(pos) <= ' ')
++pos;
return pos;
}
- static int skipTrailingWhite(String string, int start, int end) {
+ public static int skipTrailingWhite(String string, int start, int end) {
while (end > start && string.charAt(end - 1) <= ' ')
--end;
return end;
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionVector.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java
index 3d70be612..c723c8e6b 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionVector.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java
@@ -8,7 +8,7 @@
* Contributors:
* Cloudsmith Inc. - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.internal.p2.metadata;
import java.io.Serializable;
@@ -18,9 +18,13 @@ import java.io.Serializable;
*
* @Immutable
*/
-public class VersionVector implements Comparable, Serializable {
+public class VersionVector implements Comparable<VersionVector>, Serializable {
- private static final class MaxStringValue implements Comparable, Serializable {
+ interface MinMaxComparable extends Comparable<Object>, Serializable {
+ //
+ }
+
+ private static final class MaxStringValue implements MinMaxComparable {
private static final long serialVersionUID = -4936252230441132767L;
MaxStringValue() {
@@ -41,7 +45,7 @@ public class VersionVector implements Comparable, Serializable {
}
}
- private static final class MaxValue implements Comparable, Serializable {
+ private static final class MaxValue implements MinMaxComparable {
private static final long serialVersionUID = -5889641741635253589L;
MaxValue() {
@@ -52,17 +56,17 @@ public class VersionVector implements Comparable, Serializable {
return o == this ? 0 : 1;
}
- public String toString() {
- return "M"; //$NON-NLS-1$
- }
-
// For singleton deserialization
private Object readResolve() {
return MAX_VALUE;
}
+
+ public String toString() {
+ return "M"; //$NON-NLS-1$
+ }
}
- private static class MinValue implements Comparable, Serializable {
+ private static class MinValue implements MinMaxComparable {
private static final long serialVersionUID = -1066323980049812226L;
MinValue() {
@@ -73,87 +77,129 @@ public class VersionVector implements Comparable, Serializable {
return o == this ? 0 : -1;
}
- public String toString() {
- return "-M"; //$NON-NLS-1$
- }
-
private Object readResolve() {
return MIN_VALUE;
}
+
+ public String toString() {
+ return "-M"; //$NON-NLS-1$
+ }
}
/**
* A value that is greater then any other value
*/
- public static final Comparable MAX_VALUE = new MaxValue();
+ public static final Comparable<Object> MAX_VALUE = new MaxValue();
/**
* A value that is greater then any string but less then {@link #MAX_VALUE} and
* any Integer or VersionVector.
*/
- public static final Comparable MAXS_VALUE = new MaxStringValue();
+ public static final Comparable<Object> MAXS_VALUE = new MaxStringValue();
/**
* A value that is less then any other value
*/
- public static final Comparable MIN_VALUE = new MinValue();
+ public static final Comparable<Object> MIN_VALUE = new MinValue();
+
+ /**
+ * A value that is greater then {@link #MIN_VALUE} and less then any string,
+ * Integer, or VersionVector (a.k.a. empty_string)
+ */
+ public static final String MINS_VALUE = ""; //$NON-NLS-1$
private static final long serialVersionUID = -8385373304298723744L;
- static void rawToString(StringBuffer sb, boolean forRange, Comparable e) {
- if (e instanceof String) {
- writeQuotedString(sb, forRange, (String) e, '\'', 0, false);
- } else if (e instanceof VersionVector) {
- sb.append('<');
- ((VersionVector) e).toString(sb, forRange);
- sb.append('>');
- } else
- sb.append(e);
+ static int compare(Comparable<?>[] vectorA, Comparable<?> padA, Comparable<?>[] vectorB, Comparable<?> padB) {
+ int top = vectorA.length;
+ if (top > vectorB.length)
+ top = vectorB.length;
+
+ for (int idx = 0; idx < top; ++idx) {
+ int cmp = compareSegments(vectorA[idx], vectorB[idx]);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ // All elements compared equal up to this point. Check
+ // pad values
+ if (top < vectorA.length)
+ return (padB == null) ? 1 : compareReminder(top, vectorA, padA, padB);
+
+ if (top < vectorB.length)
+ return (padA == null) ? -1 : -compareReminder(top, vectorB, padB, padA);
+
+ // Lengths are equal. Compare pad values
+ return padA == null ? (padB == null ? 0 : -1) : (padB == null ? 1 : compareSegments(padA, padB));
}
- /**
- * Write a string within quotes. If the string is found to contain the quote, an attempt is made
- * to flip quote character (single quote becomes double quote and vice versa). A string that contains
- * both will be written as several adjacent quoted strings so that each string is quoted with a
- * quote character that it does not contain.
- * @param sb The buffer that will receive the string
- * @param rangeSafe Set to <code>true</code> if the resulting string will be used in a range string
- * and hence need to escape the range delimiter characters
- * @param s The string to be written
- * @param quote The quote character to start with. Must be the single or double quote character.
- * @param startPos The start position
- * @param didFlip True if the call is recursive and thus, cannot switch quotes in the first string.
- */
- static void writeQuotedString(StringBuffer sb, boolean rangeSafe, String s, char quote, int startPos, boolean didFlip) {
- int quotePos = sb.length();
- sb.append(quote);
- boolean otherSeen = false;
- int top = s.length();
- for (int idx = startPos; idx < top; ++idx) {
- char c = s.charAt(idx);
- if (c == '\'' || c == '"') {
- if (c == quote) {
- char otherQuote = quote == '\'' ? '"' : '\'';
- if (didFlip || otherSeen) {
- // We can only flip once
- sb.append(quote);
- writeQuotedString(sb, rangeSafe, s, otherQuote, idx, true);
- return;
- }
- quote = otherQuote;
- sb.setCharAt(quotePos, quote);
- didFlip = true;
- } else
- otherSeen = true;
+ static boolean equals(Comparable<?>[] vectorA, Comparable<?> padValueA, Comparable<?>[] vectorB, Comparable<?> padValueB) {
+ // We compare pad first since it is impossible for versions with
+ // different pad to be equal (versions are padded to infinity)
+ if (padValueA == null) {
+ if (padValueB != null)
+ return false;
+ } else {
+ if (padValueB == null || !padValueA.equals(padValueB))
+ return false;
+ }
+
+ int idx = vectorA.length;
+
+ // If the length of the vector differs, the versions cannot be equal
+ // since segments equal to pad are stripped by the parser
+ if (idx != vectorB.length)
+ return false;
+
+ while (--idx >= 0)
+ if (!vectorA[idx].equals(vectorB[idx]))
+ return false;
+
+ return true;
+ }
+
+ static int hashCode(Comparable<?>[] vector, Comparable<?> padValue) {
+ int hashCode = padValue == null ? 31 : padValue.hashCode();
+ int idx = vector.length;
+ while (--idx >= 0) {
+ Object elem = vector[idx];
+ if (elem != null)
+ hashCode += elem.hashCode();
+ hashCode = hashCode * 31;
+ }
+ return hashCode;
+ }
+
+ static void toString(StringBuffer sb, Comparable<?>[] vector, Comparable<?> padValue, boolean rangeSafe) {
+ int top = vector.length;
+ if (top == 0)
+ // Write one pad value as explicit. It will be considered
+ // redundant and removed by the parser but the raw format
+ // does not allow zero elements
+ VersionFormat.rawToString(sb, rangeSafe, padValue == null ? MIN_VALUE : padValue);
+ else {
+ for (int idx = 0; idx < top; ++idx) {
+ if (idx > 0)
+ sb.append('.');
+ VersionFormat.rawToString(sb, rangeSafe, vector[idx]);
}
- if (rangeSafe && (c == '\\' || c == '[' || c == '(' || c == ']' || c == ')' || c == ',' || c <= ' '))
- sb.append('\\');
- sb.append(c);
}
- sb.append(quote);
+ if (padValue != null) {
+ sb.append('p');
+ VersionFormat.rawToString(sb, rangeSafe, padValue);
+ }
+ }
+
+ private static int compareReminder(int idx, Comparable<?>[] vector, Comparable<?> padValue, Comparable<?> othersPad) {
+ int cmp;
+ for (cmp = 0; idx < vector.length && cmp == 0; ++idx)
+ cmp = compareSegments(vector[idx], othersPad);
+ if (cmp == 0)
+ cmp = (padValue == null) ? -1 : compareSegments(padValue, othersPad);
+ return cmp;
}
- private static int compareSegments(Comparable a, Comparable b) {
+ static int compareSegments(Comparable<?> a, Comparable<?> b) {
if (a == b)
return 0;
@@ -164,20 +210,20 @@ public class VersionVector implements Comparable, Serializable {
}
if (a instanceof String && b instanceof String)
- return a.compareTo(b);
+ return ((String) a).compareTo((String) b);
if (a == MAX_VALUE || a == MIN_VALUE || a == MAXS_VALUE)
- return a.compareTo(b);
+ return ((MinMaxComparable) a).compareTo(b);
if (b == MAX_VALUE || b == MIN_VALUE || b == MAXS_VALUE)
- return -b.compareTo(a);
+ return -((MinMaxComparable) b).compareTo(a);
if (a instanceof Integer)
return 1;
if (b instanceof Integer)
return -1;
if (a instanceof VersionVector)
- return (b instanceof VersionVector) ? a.compareTo(b) : 1;
+ return (b instanceof VersionVector) ? ((VersionVector) a).compareTo((VersionVector) b) : 1;
if (b instanceof VersionVector)
return -1;
@@ -185,46 +231,20 @@ public class VersionVector implements Comparable, Serializable {
throw new IllegalArgumentException();
}
- private Comparable padValue;
-
- private Comparable[] vector;
+ private final Comparable<?> padValue;
- VersionVector() {
- // Constructor used in conjunction with init (when version is parsed from string)
- }
+ private final Comparable<?>[] vector;
- VersionVector(Comparable[] vector, Comparable pad) {
+ public VersionVector(Comparable<?>[] vector, Comparable<?> pad) {
this.vector = vector;
this.padValue = (pad == MIN_VALUE) ? null : pad;
}
- public int compareTo(Object o) {
- if (o == this)
+ public int compareTo(VersionVector ov) {
+ if (ov == this)
return 0;
- VersionVector ov = (VersionVector) o;
- Comparable[] t_vector = vector;
- Comparable[] o_vector = ov.vector;
- int top = t_vector.length;
- if (top > o_vector.length)
- top = o_vector.length;
-
- for (int idx = 0; idx < top; ++idx) {
- int cmp = compareSegments(t_vector[idx], o_vector[idx]);
- if (cmp != 0)
- return cmp;
- }
-
- // All elements compared equal up to this point. Check
- // pad values
- if (top < t_vector.length)
- return (ov.padValue == null) ? 1 : compareReminder(top, ov.padValue);
-
- if (top < o_vector.length)
- return (padValue == null) ? -1 : -ov.compareReminder(top, padValue);
-
- // Lengths are equal. Compare pad values
- return padValue == null ? (ov.padValue == null ? 0 : -1) : (ov.padValue == null ? 1 : compareSegments(padValue, ov.padValue));
+ return compare(vector, padValue, ov.vector, ov.padValue);
}
public boolean equals(Object o) {
@@ -235,31 +255,7 @@ public class VersionVector implements Comparable, Serializable {
return false;
VersionVector ov = (VersionVector) o;
-
- // We compare pad first since it is impossible for versions with
- // different pad to be equal (versions are padded to infinity)
- if (padValue == null) {
- if (ov.padValue != null)
- return false;
- } else {
- if (ov.padValue == null || !padValue.equals(ov.padValue))
- return false;
- }
-
- Comparable[] t_vector = vector;
- Comparable[] o_vector = ov.vector;
- int idx = t_vector.length;
-
- // If the length of the vector differs, the versions cannot be equal
- // since segments equal to pad are stripped by the parser
- if (idx != o_vector.length)
- return false;
-
- while (--idx >= 0)
- if (!t_vector[idx].equals(o_vector[idx]))
- return false;
-
- return true;
+ return equals(vector, padValue, ov.vector, ov.padValue);
}
/**
@@ -267,7 +263,7 @@ public class VersionVector implements Comparable, Serializable {
* versions that has a raw vector with a larger number of elements
* @return The pad value or <code>null</code> if not set.
*/
- public Comparable getPad() {
+ public Comparable<?> getPad() {
return padValue;
}
@@ -276,7 +272,7 @@ public class VersionVector implements Comparable, Serializable {
* @param index The zero based index of the desired element
* @return An element from the raw vector
*/
- public Comparable getSegment(int index) {
+ public Comparable<?> getSegment(int index) {
return vector[index];
}
@@ -288,16 +284,17 @@ public class VersionVector implements Comparable, Serializable {
return vector.length;
}
+ /**
+ * This method is package protected since it violates the immutable
+ * contract.
+ * @return The raw vector. Must be treated as read-only
+ */
+ Comparable<?>[] getVector() {
+ return vector;
+ }
+
public int hashCode() {
- int hashCode = padValue == null ? 31 : padValue.hashCode();
- int idx = vector.length;
- while (--idx >= 0) {
- Object elem = vector[idx];
- if (elem != null)
- hashCode += elem.hashCode();
- hashCode = hashCode * 31;
- }
- return hashCode;
+ return hashCode(vector, padValue);
}
public String toString() {
@@ -312,7 +309,7 @@ public class VersionVector implements Comparable, Serializable {
* @param sb The buffer to append to
*/
public void toString(StringBuffer sb) {
- toString(sb, false);
+ toString(sb, vector, padValue, false);
}
/**
@@ -322,46 +319,7 @@ public class VersionVector implements Comparable, Serializable {
* @param rangeSafe If <code>true</code>, the range delimiters will be escaped
* with backslash.
*/
- public void toString(StringBuffer sb, boolean rangeSafe) {
- int top = vector.length;
- if (top == 0)
- // Write one pad value as explicit. It will be considered
- // redundant and removed by the parser but the raw format
- // does not allow zero elements
- rawToString(sb, rangeSafe, padValue == null ? MIN_VALUE : padValue);
- else {
- for (int idx = 0; idx < top; ++idx) {
- if (idx > 0)
- sb.append('.');
- rawToString(sb, rangeSafe, vector[idx]);
- }
- }
- if (padValue != null) {
- sb.append('p');
- rawToString(sb, rangeSafe, padValue);
- }
- }
-
- /**
- * This method is package protected since it violates the immutable
- * contract.
- * @return The raw vector. Must be treated as read-only
- */
- Comparable[] getVector() {
- return vector;
- }
-
- void init(Comparable[] vec, Comparable pad) {
- vector = vec;
- padValue = (pad == MIN_VALUE) ? null : pad;
- }
-
- private int compareReminder(int idx, Comparable othersPad) {
- int cmp;
- for (cmp = 0; idx < vector.length && cmp == 0; ++idx)
- cmp = compareSegments(vector[idx], othersPad);
- if (cmp == 0)
- cmp = (padValue == null) ? -1 : padValue.compareTo(othersPad);
- return cmp;
+ void toString(StringBuffer sb, boolean rangeSafe) {
+ toString(sb, vector, padValue, rangeSafe);
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionedId.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionedId.java
index 3b97f48a1..1bf5ebd44 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionedId.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionedId.java
@@ -11,9 +11,12 @@
* Thomas Hallgren - Fix for bug 268659
* IBM - ongoing development
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.internal.p2.metadata;
+
+import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.internal.p2.core.helpers.StringHelper;
+import org.eclipse.equinox.p2.metadata.IVersionedId;
/**
* An object representing a (id,version) pair.
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/All.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/All.java
new file mode 100644
index 000000000..4de975525
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/All.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * A collection filter that yields true if the <code>filter</code> yields true for
+ * all of the elements of the <code>collection</code>
+ */
+final class All extends CollectionFilter {
+ All(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ protected Object evaluate(IEvaluationContext context, Iterator<?> itor) {
+ Variable variable = lambda.getItemVariable();
+ while (itor.hasNext()) {
+ variable.setValue(context, itor.next());
+ if (lambda.evaluate(context) != Boolean.TRUE)
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ public int getExpressionType() {
+ return TYPE_ALL;
+ }
+
+ public String getOperator() {
+ return KEYWORD_ALL;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/And.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/And.java
new file mode 100644
index 000000000..7e8adf20e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/And.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * n-ary AND operator. The full evaluation is <code>true</code> if all its operands evaluate to
+ * <code>true</code>.
+ */
+final class And extends NAry {
+ And(Expression[] operands) {
+ super(assertLength(operands, 2, OPERATOR_AND));
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ for (int idx = 0; idx < operands.length; ++idx) {
+ if (operands[idx].evaluate(context) != Boolean.TRUE)
+ return Boolean.FALSE;
+ }
+ return Boolean.TRUE;
+ }
+
+ public int getExpressionType() {
+ return TYPE_AND;
+ }
+
+ public String getOperator() {
+ return OPERATOR_AND;
+ }
+
+ public int getPriority() {
+ return PRIORITY_AND;
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ buf.append("(&"); //$NON-NLS-1$
+ for (int idx = 0; idx < operands.length; ++idx)
+ operands[idx].toLDAPString(buf);
+ buf.append(')');
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/At.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/At.java
new file mode 100644
index 000000000..e089c1dbc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/At.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.metadata.expression.Member.DynamicMember;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * This class represents indexed or keyed access to an indexed collection
+ * or a map.
+ */
+public class At extends Binary {
+ protected At(Expression lhs, Expression rhs) {
+ super(lhs, rhs);
+ }
+
+ protected Object handleMember(IEvaluationContext context, Member member, Object instance, boolean[] handled) {
+ if (instance instanceof IInstallableUnit) {
+ if ("properties".equals(member.getName())) { //$NON-NLS-1$
+ // Avoid full copy of the properties map just to get one member
+ handled[0] = true;
+ return ((IInstallableUnit) instance).getProperty((String) rhs.evaluate(context));
+ }
+ }
+ return null;
+ }
+
+ public Object evaluate(org.eclipse.equinox.p2.metadata.expression.IEvaluationContext context) {
+ Object lval;
+ if (lhs instanceof DynamicMember) {
+ DynamicMember lm = (DynamicMember) lhs;
+ Object instance = lm.operand.evaluate(context);
+ boolean[] handled = new boolean[] {false};
+ Object result = handleMember(context, lm, instance, handled);
+ if (handled[0])
+ return result;
+ lval = lm.invoke(instance);
+ } else
+ lval = lhs.evaluate(context);
+
+ Object rval = rhs.evaluate(context);
+ if (lval == null)
+ throw new IllegalArgumentException("Unable to use [] on null"); //$NON-NLS-1$
+
+ if (lval instanceof Map<?, ?>)
+ return ((Map<?, ?>) lval).get(rval);
+
+ if (rval instanceof Number) {
+ if (lval instanceof List<?>)
+ return ((List<?>) lval).get(((Number) rval).intValue());
+ if (lval != null && lval.getClass().isArray())
+ return ((Object[]) lval)[((Number) rval).intValue()];
+ }
+
+ if (lval instanceof Dictionary<?, ?>)
+ return ((Dictionary<?, ?>) lval).get(rval);
+
+ throw new IllegalArgumentException("Unable to use [] on a " + lval.getClass().getName()); //$NON-NLS-1$
+ }
+
+ public Iterator<?> evaluateAsIterator(IEvaluationContext context) {
+ Object value = evaluate(context);
+ if (!(value instanceof Iterator<?>))
+ value = RepeatableIterator.create(value);
+ return (Iterator<?>) value;
+ }
+
+ public int getExpressionType() {
+ return TYPE_AT;
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ appendOperand(bld, rootVariable, lhs, getPriority());
+ bld.append('[');
+ appendOperand(bld, rootVariable, rhs, PRIORITY_MAX);
+ bld.append(']');
+ }
+
+ public String getOperator() {
+ return OPERATOR_AT;
+ }
+
+ public int getPriority() {
+ return PRIORITY_MEMBER;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Binary.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Binary.java
new file mode 100644
index 000000000..f4f7da504
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Binary.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.internal.p2.metadata.expression.Member.DynamicMember;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.expression.IExpressionVisitor;
+
+/**
+ * The abstract base class for all binary operations
+ */
+public abstract class Binary extends Expression {
+ public final Expression lhs;
+
+ public final Expression rhs;
+
+ protected Binary(Expression lhs, Expression rhs) {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && lhs.accept(visitor) && rhs.accept(visitor);
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0) {
+ Binary be = (Binary) e;
+ cmp = lhs.compareTo(be.lhs);
+ if (cmp == 0)
+ cmp = rhs.compareTo(be.rhs);
+ }
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ if (super.equals(o)) {
+ Binary bo = (Binary) o;
+ return lhs.equals(bo.lhs) && rhs.equals(bo.rhs);
+ }
+ return false;
+ }
+
+ public int getPriority() {
+ return PRIORITY_BINARY; // Default priority
+ }
+
+ public int hashCode() {
+ int result = 31 + lhs.hashCode();
+ return 31 * result + rhs.hashCode();
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ appendOperand(bld, rootVariable, lhs, getPriority());
+ bld.append(' ');
+ bld.append(getOperator());
+ bld.append(' ');
+ appendOperand(bld, rootVariable, rhs, getPriority());
+ }
+
+ /**
+ * Appends the LDAP filter attribute name from the lhs expression if
+ * possible.
+ * @throws UnsupportedOperationException when this expression does not conform to an
+ * LDAP filter binary expression
+ */
+ void appendLDAPAttribute(StringBuffer buf) {
+ if (lhs instanceof DynamicMember) {
+ DynamicMember attr = (DynamicMember) lhs;
+ if (attr.operand instanceof Variable) {
+ buf.append(attr.getName());
+ return;
+ }
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ private static char hexChar(int value) {
+ return (char) (value < 10 ? ('0' + value) : ('a' + (value - 10)));
+ }
+
+ static void appendLDAPEscaped(StringBuffer bld, String str) {
+ appendLDAPEscaped(bld, str, true);
+ }
+
+ static void appendLDAPEscaped(StringBuffer bld, String str, boolean escapeWild) {
+ int top = str.length();
+ for (int idx = 0; idx < top; ++idx) {
+ char c = str.charAt(idx);
+ if (!escapeWild) {
+ if (c == '*') {
+ bld.append(c);
+ continue;
+ } else if (c == '\\' && idx + 1 < top && str.charAt(idx + 1) == '*') {
+ bld.append("\\2a"); //$NON-NLS-1$
+ ++idx;
+ continue;
+ }
+ }
+ if (c == '(' || c == ')' || c == '*' || c == '\\' || c < ' ' || c > 127) {
+ short cs = (short) c;
+ bld.append('\\');
+ bld.append(hexChar((cs & 0x00f0) >> 4));
+ bld.append(hexChar(cs & 0x000f));
+ } else
+ bld.append(c);
+ }
+ }
+
+ /**
+ * Appends the LDAP filter value from the rhs expression if
+ * possible.
+ * @throws UnsupportedOperationException when this expression does not conform to an
+ * LDAP filter binary expression
+ */
+ void appendLDAPValue(StringBuffer buf) {
+ if (rhs instanceof Literal) {
+ Object value = rhs.evaluate(null);
+ if (value instanceof String || value instanceof Version) {
+ appendLDAPEscaped(buf, value.toString());
+ return;
+ }
+ }
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java
new file mode 100644
index 000000000..c69e75e32
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CoercingComparator.java
@@ -0,0 +1,392 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Comparator;
+import org.eclipse.equinox.internal.p2.metadata.MetadataActivator;
+import org.eclipse.equinox.p2.metadata.Version;
+
+/**
+ * A comparator that performs coercion if needed before comparison.
+ * @param <T> The type for the comparator.
+ */
+public abstract class CoercingComparator<T> {
+ static class BooleanCoercer extends CoercingComparator<Boolean> {
+ public int compare(Boolean o1, Boolean o2) {
+ return o1.booleanValue() == o2.booleanValue() ? 0 : (o1.booleanValue() ? 1 : -1);
+ }
+
+ @Override
+ Boolean coerce(Object v) {
+ if (v instanceof Boolean)
+ return (Boolean) v;
+ if (v instanceof String) {
+ String sv = ((String) v).trim();
+ if (sv.equalsIgnoreCase("true")) //$NON-NLS-1$
+ return Boolean.TRUE;
+ if (sv.equalsIgnoreCase("false")) //$NON-NLS-1$
+ return Boolean.FALSE;
+ }
+ throw uncoercable(v);
+ }
+
+ @Override
+ Class<Boolean> getCoerceClass() {
+ return Boolean.class;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 7;
+ }
+ }
+
+ static class ClassCoercer extends CoercingComparator<Class<?>> {
+ public int compare(Class<?> o1, Class<?> o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+
+ @Override
+ Class<?> coerce(Object v) {
+ if (v instanceof Class<?>)
+ return (Class<?>) v;
+ if (v instanceof String) {
+ try {
+ return MetadataActivator.context.getBundle().loadClass(((String) v).trim());
+ } catch (Exception e) {
+ //
+ }
+ }
+ throw uncoercable(v);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ Class<Class<?>> getCoerceClass() {
+ Class<?> cls = Class.class;
+ return (Class<Class<?>>) cls;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 11;
+ }
+ }
+
+ static class FromStringCoercer<T extends Comparable<Object>> extends CoercingComparator<T> {
+ private final Class<T> coerceClass;
+ private final Constructor<T> constructor;
+
+ public FromStringCoercer(Class<T> coerceClass, Constructor<T> constructor) {
+ this.coerceClass = coerceClass;
+ this.constructor = constructor;
+ }
+
+ @Override
+ T coerce(Object v) {
+ if (v instanceof String) {
+ try {
+ return constructor.newInstance(new Object[] {((String) v).trim()});
+ } catch (Exception e) {
+ //
+ }
+ }
+ throw uncoercable(v);
+ }
+
+ @Override
+ int compare(T o1, T o2) {
+ return o1.compareTo(o2);
+ }
+
+ @Override
+ Class<T> getCoerceClass() {
+ return coerceClass;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 0;
+ }
+ }
+
+ static class IntegerCoercer extends CoercingComparator<Integer> {
+ public int compare(Integer o1, Integer o2) {
+ return o1.compareTo(o2);
+ }
+
+ @Override
+ Integer coerce(Object v) {
+ if (v instanceof Integer)
+ return (Integer) v;
+ if (v instanceof Number)
+ return new Integer(((Number) v).intValue());
+ if (v instanceof String) {
+ try {
+ return Integer.valueOf(((String) v).trim());
+ } catch (NumberFormatException e) {
+ //
+ }
+ }
+ throw uncoercable(v);
+ }
+
+ @Override
+ Class<Integer> getCoerceClass() {
+ return Integer.class;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 6;
+ }
+ }
+
+ static class LongCoercer extends CoercingComparator<Long> {
+ public int compare(Long o1, Long o2) {
+ return o1.compareTo(o2);
+ }
+
+ @Override
+ Long coerce(Object v) {
+ if (v instanceof Long)
+ return (Long) v;
+ if (v instanceof Number)
+ return new Long(((Number) v).longValue());
+ if (v instanceof String) {
+ try {
+ return Long.valueOf(((String) v).trim());
+ } catch (NumberFormatException e) {
+ //
+ }
+ }
+ throw uncoercable(v);
+ }
+
+ @Override
+ Class<Long> getCoerceClass() {
+ return Long.class;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 5;
+ }
+ }
+
+ static class StringCoercer extends CoercingComparator<String> {
+ public int compare(String o1, String o2) {
+ return o1.compareTo(o2);
+ }
+
+ @Override
+ String coerce(Object v) {
+ if (v instanceof Class<?>)
+ return ((Class<?>) v).getName();
+ return v.toString();
+ }
+
+ @Override
+ Class<String> getCoerceClass() {
+ return String.class;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 10;
+ }
+ }
+
+ static class VersionCoercer extends CoercingComparator<Version> {
+ public int compare(Version o1, Version o2) {
+ return o1.compareTo(o2);
+ }
+
+ boolean canCoerceTo(Class<?> cls) {
+ return Version.class.isAssignableFrom(cls);
+ }
+
+ @Override
+ Version coerce(Object v) {
+ if (v instanceof Version)
+ return (Version) v;
+ if (v instanceof String)
+ return Version.create((String) v);
+ if (v instanceof String) {
+ try {
+ return Version.create((String) v);
+ } catch (NumberFormatException e) {
+ //
+ }
+ }
+ throw uncoercable(v);
+ }
+
+ @Override
+ Class<Version> getCoerceClass() {
+ return Version.class;
+ }
+
+ @Override
+ int getCoercePrio() {
+ return 1;
+ }
+ }
+
+ private static class SetAccessibleAction implements PrivilegedAction<Object> {
+ private final AccessibleObject accessible;
+
+ SetAccessibleAction(AccessibleObject accessible) {
+ this.accessible = accessible;
+ }
+
+ public Object run() {
+ accessible.setAccessible(true);
+ return null;
+ }
+ }
+
+ private static CoercingComparator<?>[] coercers = {new ClassCoercer(), new BooleanCoercer(), new LongCoercer(), new IntegerCoercer(), new VersionCoercer(), new StringCoercer()};
+
+ private static final Class<?>[] constructorType = new Class<?>[] {String.class};
+
+ /**
+ * Finds the comparator for <code>a</code> and <code>b</code> and delegates the coercion/comparison to the comparator
+ * according to priority.
+ * @param o1 the first object to be compared.
+ * @param o2 the second object to be compared.
+ * @return The result of the comparison
+ * @throws IllegalArgumentException if no comparator was found or if coercion was impossible
+ * @see Comparator#compare(Object, Object)
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public static <TA extends Object, TB extends Object> int coerceAndCompare(TA o1, TB o2) throws IllegalArgumentException {
+ if (o1 == null || o2 == null)
+ throw new IllegalArgumentException("Cannot compare null to anything"); //$NON-NLS-1$
+
+ if (o1 instanceof Comparable && o1.getClass().isAssignableFrom(o2.getClass()))
+ return ((Comparable) o1).compareTo(o2);
+
+ if (o2 instanceof Comparable && o2.getClass().isAssignableFrom(o1.getClass()))
+ return -((Comparable) o2).compareTo(o1);
+
+ CoercingComparator<TA> ca = getComparator(o1, o2);
+ CoercingComparator<TB> cb = getComparator(o2, o1);
+ return ca.getCoercePrio() <= cb.getCoercePrio() ? ca.compare(o1, ca.coerce(o2)) : cb.compare(cb.coerce(o1), o2);
+ }
+
+ /**
+ * Finds the comparator for <code>a</code> and <code>b</code> and delegates the coercion/equal to the comparator
+ * according to priority.
+ * @param o1 the first object to be compared.
+ * @param o2 the second object to be compared.
+ * @return The result of the equality test
+ * @throws IllegalArgumentException if no comparator was found or if coercion was impossible
+ * @see Object#equals(Object)
+ */
+ public static <TA extends Object, TB extends Object> boolean coerceAndEquals(TA o1, TB o2) throws IllegalArgumentException {
+ if (o1 == o2)
+ return true;
+
+ if (o1 == null || o2 == null)
+ return false;
+
+ if (o1.getClass() != o2.getClass()) {
+ if (o1.getClass().isAssignableFrom(o2.getClass()))
+ return o1.equals(o2);
+ if (o2.getClass().isAssignableFrom(o1.getClass()))
+ return o2.equals(o1);
+ try {
+ CoercingComparator<TA> ca = getComparator(o1, o2);
+ CoercingComparator<TB> cb = getComparator(o2, o1);
+ return ca.getCoercePrio() <= cb.getCoercePrio() ? o1.equals(ca.coerce(o2)) : o2.equals(cb.coerce(o1));
+ } catch (IllegalArgumentException e) {
+ //
+ }
+ }
+ return o1.equals(o2);
+ }
+
+ /**
+ * Obtains the coercing comparator for the given <code>value</code>.
+ * @param value The value
+ * @return The coercing comparator
+ */
+ @SuppressWarnings("unchecked")
+ public static <V extends Object> CoercingComparator<V> getComparator(V value, Object v2) {
+ Class<V> vClass = (Class<V>) value.getClass();
+ CoercingComparator<?>[] carr = coercers;
+ int idx = carr.length;
+ while (--idx >= 0) {
+ CoercingComparator<?> c = carr[idx];
+ if (c.canCoerceTo(vClass)) {
+ CoercingComparator<V> cv = (CoercingComparator<V>) c;
+ return cv;
+ }
+ }
+
+ if (value instanceof Comparable<?> && v2 instanceof String) {
+ Class<Comparable<Object>> cClass = (Class<Comparable<Object>>) vClass;
+ Constructor<Comparable<Object>> constructor;
+ try {
+ constructor = cClass.getConstructor(constructorType);
+ if (!constructor.isAccessible())
+ AccessController.doPrivileged(new SetAccessibleAction(constructor));
+ synchronized (CoercingComparator.class) {
+ int top = coercers.length;
+ CoercingComparator<?>[] nc = new CoercingComparator<?>[top + 1];
+ System.arraycopy(coercers, 0, nc, 1, top);
+ CoercingComparator<V> cv = (CoercingComparator<V>) new FromStringCoercer<Comparable<Object>>(cClass, constructor);
+ nc[0] = cv;
+ coercers = nc;
+ return cv;
+ }
+ } catch (Exception e) {
+ //
+ }
+ }
+ throw new IllegalArgumentException("No comparator for " + vClass.getName()); //$NON-NLS-1$
+ }
+
+ protected IllegalArgumentException uncoercable(Object v) {
+ StringBuffer sb = new StringBuffer("Cannot coerce "); //$NON-NLS-1$
+ if (v instanceof String) {
+ sb.append('\'');
+ sb.append(v);
+ sb.append('\'');
+ } else if (v instanceof Number) {
+ sb.append("number "); //$NON-NLS-1$
+ sb.append(v);
+ } else {
+ sb.append("an object of instance "); //$NON-NLS-1$
+ sb.append(v.getClass().getName());
+ }
+ sb.append(" into a "); //$NON-NLS-1$
+ sb.append(getCoerceClass().getName());
+ return new IllegalArgumentException(sb.toString());
+ }
+
+ boolean canCoerceTo(Class<?> cls) {
+ return cls == getCoerceClass();
+ }
+
+ abstract T coerce(Object v);
+
+ abstract int compare(T o1, T o2);
+
+ abstract Class<T> getCoerceClass();
+
+ abstract int getCoercePrio();
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CollectionFilter.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CollectionFilter.java
new file mode 100644
index 000000000..bb845d373
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/CollectionFilter.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+import org.eclipse.equinox.p2.metadata.expression.IExpressionVisitor;
+
+/**
+ * Some kind of operation that is performed for each element of a collection. I.e.
+ * <code>x.&lt;operation&gt;(y | &lt;expression&rt;)</code>
+ */
+public abstract class CollectionFilter extends Unary {
+ public static void appendProlog(StringBuffer bld, Variable rootVariable, Expression lhs, String operator) {
+ if (lhs != rootVariable) {
+ appendOperand(bld, rootVariable, lhs, PRIORITY_COLLECTION);
+ bld.append('.');
+ }
+ bld.append(operator);
+ bld.append('(');
+ }
+
+ public final LambdaExpression lambda;
+
+ protected CollectionFilter(Expression collection, LambdaExpression lambda) {
+ super(collection);
+ this.lambda = lambda;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && lambda.accept(visitor);
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0)
+ cmp = lambda.compareTo(((CollectionFilter) e).lambda);
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ return super.equals(o) && lambda.equals(((CollectionFilter) o).lambda);
+ }
+
+ public final Object evaluate(IEvaluationContext context) {
+ Iterator<?> lval = operand.evaluateAsIterator(context);
+ context = lambda.prolog(context);
+ return evaluate(context, lval);
+ }
+
+ public final Iterator<?> evaluateAsIterator(IEvaluationContext context) {
+ Iterator<?> lval = operand.evaluateAsIterator(context);
+ context = lambda.prolog(context);
+ return evaluateAsIterator(context, lval);
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ appendProlog(bld, rootVariable, operand, getOperator());
+ appendOperand(bld, rootVariable, lambda, PRIORITY_LAMBDA);
+ bld.append(')');
+ }
+
+ public int hashCode() {
+ int result = 31 + operand.hashCode();
+ return 31 * result + lambda.hashCode();
+ }
+
+ public int getPriority() {
+ return PRIORITY_COLLECTION;
+ }
+
+ protected abstract Object evaluate(final IEvaluationContext context, Iterator<?> iterator);
+
+ protected Iterator<?> evaluateAsIterator(IEvaluationContext context, Iterator<?> iterator) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Compare.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Compare.java
new file mode 100644
index 000000000..7e958e41d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Compare.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * Comparisons for magnitude.
+ */
+final class Compare extends Binary {
+ static IllegalArgumentException uncomparable(Object lval, Object rval) {
+ return new IllegalArgumentException("Cannot compare a " + lval.getClass().getName() + " to a " + rval.getClass().getName()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ final boolean compareLess;
+
+ final boolean equalOK;
+
+ Compare(Expression lhs, Expression rhs, boolean compareLess, boolean equalOK) {
+ super(lhs, rhs);
+ this.compareLess = compareLess;
+ this.equalOK = equalOK;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ int cmpResult = CoercingComparator.coerceAndCompare(lhs.evaluate(context), rhs.evaluate(context));
+ return Boolean.valueOf(cmpResult == 0 ? equalOK : (cmpResult < 0 ? compareLess : !compareLess));
+ }
+
+ public int getExpressionType() {
+ return compareLess ? (equalOK ? TYPE_LESS_EQUAL : TYPE_LESS) : (equalOK ? TYPE_GREATER_EQUAL : TYPE_GREATER);
+ }
+
+ public String getOperator() {
+ return compareLess ? (equalOK ? OPERATOR_LT_EQUAL : OPERATOR_LT) : (equalOK ? OPERATOR_GT_EQUAL : OPERATOR_GT);
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ if (!equalOK)
+ buf.append("(!"); //$NON-NLS-1$
+ buf.append('(');
+ appendLDAPAttribute(buf);
+ if (equalOK)
+ buf.append(compareLess ? OPERATOR_LT_EQUAL : OPERATOR_GT_EQUAL);
+ else
+ buf.append(compareLess ? OPERATOR_GT_EQUAL : OPERATOR_LT_EQUAL);
+ appendLDAPValue(buf);
+ buf.append(')');
+ if (!equalOK)
+ buf.append(')');
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Equals.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Equals.java
new file mode 100644
index 000000000..7452c3165
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Equals.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * An expression that performs the == and != comparisons
+ */
+final class Equals extends Binary {
+ final boolean negate;
+
+ Equals(Expression lhs, Expression rhs, boolean negate) {
+ super(lhs, rhs);
+ this.negate = negate;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ boolean result = CoercingComparator.coerceAndEquals(lhs.evaluate(context), rhs.evaluate(context));
+ if (negate)
+ result = !result;
+ return Boolean.valueOf(result);
+ }
+
+ public int getExpressionType() {
+ return negate ? TYPE_NOT_EQUALS : TYPE_EQUALS;
+ }
+
+ public String getOperator() {
+ return negate ? OPERATOR_NOT_EQUALS : OPERATOR_EQUALS;
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ if (negate)
+ buf.append("(!"); //$NON-NLS-1$
+ buf.append('(');
+ appendLDAPAttribute(buf);
+ buf.append('=');
+ appendLDAPValue(buf);
+ buf.append(')');
+ if (negate)
+ buf.append(')');
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/EvaluationContext.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/EvaluationContext.java
new file mode 100644
index 000000000..8fb3665bc
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/EvaluationContext.java
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+import org.eclipse.equinox.p2.metadata.expression.IExpression;
+
+/**
+ * Highly specialized evaluation contexts optimized for misc purposes
+ */
+public class EvaluationContext implements IEvaluationContext {
+ public static class Parameters extends EvaluationContext {
+ private static final Object[] noParameters = new Object[0];
+
+ private final Object[] parameters;
+
+ public Parameters(IEvaluationContext parentContext, Object[] parameters) {
+ super(parentContext);
+ this.parameters = parameters == null ? noParameters : parameters;
+ }
+
+ public Object getParameter(int position) {
+ return position >= 0 && position < parameters.length ? parameters[position] : super.getParameter(position);
+ }
+ }
+
+ public static class SingleVariableContext implements IEvaluationContext {
+ private final IEvaluationContext parentContext;
+
+ private Object value;
+
+ private final IExpression variable;
+
+ public SingleVariableContext(IEvaluationContext parentContext, IExpression variable) {
+ this.parentContext = parentContext;
+ this.variable = variable;
+ }
+
+ public Object getParameter(int position) {
+ return parentContext.getParameter(position);
+ }
+
+ public Object getValue(IExpression var) {
+ return variable == var ? value : parentContext.getValue(var);
+ }
+
+ public void setValue(IExpression var, Object val) {
+ if (variable == var)
+ value = val;
+ else
+ parentContext.setValue(var, val);
+ }
+ }
+
+ static class MultiVariableContext implements IEvaluationContext {
+ private final IEvaluationContext parentContext;
+
+ private final Object[] values;
+
+ public MultiVariableContext(IEvaluationContext parentContext, IExpression[] variables) {
+ this.parentContext = parentContext;
+ values = new Object[variables.length * 2];
+ for (int idx = 0, ndx = 0; ndx < variables.length; ++ndx, idx += 2)
+ values[idx] = variables[ndx];
+ }
+
+ public Object getParameter(int position) {
+ return parentContext.getParameter(position);
+ }
+
+ public Object getValue(IExpression variable) {
+ for (int idx = 0; idx < values.length; ++idx)
+ if (values[idx++] == variable)
+ return values[idx];
+ return parentContext.getValue(variable);
+ }
+
+ public void setValue(IExpression variable, Object value) {
+ for (int idx = 0; idx < values.length; ++idx)
+ if (values[idx++] == variable) {
+ values[idx] = value;
+ return;
+ }
+ parentContext.setValue(variable, value);
+ }
+ }
+
+ public static final EvaluationContext INSTANCE = new EvaluationContext(null);
+
+ public static IEvaluationContext create() {
+ return INSTANCE;
+ }
+
+ public static IEvaluationContext create(IEvaluationContext parent, IExpression variable) {
+ return new SingleVariableContext(parent, variable);
+ }
+
+ public static IEvaluationContext create(IEvaluationContext parent, IExpression[] variables) {
+ return variables.length == 1 ? new SingleVariableContext(parent, variables[0]) : new MultiVariableContext(parent, variables);
+ }
+
+ public static IEvaluationContext create(IEvaluationContext parent, Object[] parameters) {
+ return new Parameters(parent, parameters);
+ }
+
+ public static IEvaluationContext create(IExpression variable) {
+ return new SingleVariableContext(null, variable);
+ }
+
+ public static IEvaluationContext create(IExpression[] variables) {
+ if (variables == null || variables.length == 0)
+ return INSTANCE;
+ return variables.length == 1 ? create(variables[0]) : new MultiVariableContext(INSTANCE, variables);
+ }
+
+ public static IEvaluationContext create(Object[] parameters, IExpression variable) {
+ return parameters == null || parameters.length == 0 ? create(variable) : new SingleVariableContext(new Parameters(null, parameters), variable);
+ }
+
+ public static IEvaluationContext create(Object[] parameters, IExpression[] variables) {
+ if (parameters == null || parameters.length == 0)
+ return create(variables);
+
+ Parameters pctx = new Parameters(null, parameters);
+ if (variables == null || variables.length == 0)
+ return pctx;
+ return create(pctx, variables);
+ }
+
+ protected EvaluationContext(IEvaluationContext parentContext) {
+ this.parentContext = parentContext;
+ }
+
+ private final IEvaluationContext parentContext;
+
+ public Object getParameter(int position) {
+ if (parentContext == null)
+ throw new IllegalArgumentException("No such parameter: $" + position); //$NON-NLS-1$
+ return parentContext.getParameter(position);
+ }
+
+ public Object getValue(IExpression variable) {
+ if (parentContext == null)
+ throw new IllegalArgumentException("No such variable: " + variable); //$NON-NLS-1$
+ return parentContext.getValue(variable);
+ }
+
+ public void setValue(IExpression variable, Object value) {
+ if (parentContext == null)
+ throw new IllegalArgumentException("No such variable: " + variable); //$NON-NLS-1$
+ parentContext.setValue(variable, value);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Exists.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Exists.java
new file mode 100644
index 000000000..14fbc5df4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Exists.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * A collection filter that yields true if the <code>filter</code> yields true for
+ * any of the elements of the <code>collection</code>
+ */
+final class Exists extends CollectionFilter {
+ Exists(Expression collection, LambdaExpression lambda) {
+ super(collection, lambda);
+ }
+
+ protected Object evaluate(IEvaluationContext context, Iterator<?> itor) {
+ Variable variable = lambda.getItemVariable();
+ while (itor.hasNext()) {
+ variable.setValue(context, itor.next());
+ if (lambda.evaluate(context) == Boolean.TRUE)
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ public int getExpressionType() {
+ return TYPE_EXISTS;
+ }
+
+ public String getOperator() {
+ return KEYWORD_EXISTS;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java
new file mode 100644
index 000000000..75f47dd94
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Expression.java
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.metadata.expression.*;
+
+/**
+ * The base class of the expression tree.
+ */
+public abstract class Expression implements IExpression, Comparable<Expression>, IExpressionConstants {
+
+ static final Expression[] emptyArray = new Expression[0];
+
+ public static void appendOperand(StringBuffer bld, Variable rootVariable, Expression operand, int priority) {
+ if (priority < operand.getPriority()) {
+ bld.append('(');
+ operand.toString(bld, rootVariable);
+ bld.append(')');
+ } else
+ operand.toString(bld, rootVariable);
+ }
+
+ public static Expression[] assertLength(Expression[] operands, int minLength, int maxLength, String operand) {
+ if (operands == null)
+ operands = emptyArray;
+ if (operands.length < minLength)
+ throw new IllegalArgumentException("Not enough operands for " + operand); //$NON-NLS-1$
+ if (operands.length > maxLength)
+ throw new IllegalArgumentException("Too many operands for " + operand); //$NON-NLS-1$
+ return operands;
+ }
+
+ public static Expression[] assertLength(Expression[] operands, int length, String operand) {
+ if (operands == null)
+ operands = emptyArray;
+ if (operands.length < length)
+ throw new IllegalArgumentException("Not enough operands for " + operand); //$NON-NLS-1$
+ return operands;
+ }
+
+ public static int compare(Expression[] arr1, Expression[] arr2) {
+ int max = arr1.length;
+ if (max > arr2.length)
+ max = arr2.length;
+ for (int idx = 0; idx < max; ++idx) {
+ int cmp = arr1[idx].compareTo(arr2[idx]);
+ if (cmp != 0)
+ return cmp;
+ }
+ if (max == arr2.length) {
+ if (max < arr1.length)
+ return 1;
+ return 0;
+ }
+ return -1;
+ }
+
+ public static boolean equals(Expression[] arr1, Expression[] arr2) {
+ int idx = arr1.length;
+ if (idx != arr2.length)
+ return false;
+ while (--idx >= 0)
+ if (!arr1[idx].equals(arr2[idx]))
+ return false;
+ return true;
+ }
+
+ public static int hashCode(Expression[] arr) {
+ int idx = arr.length;
+ int result = 1;
+ while (--idx >= 0)
+ result = 31 * result + arr[idx].hashCode();
+ return result;
+ }
+
+ public static void elementsToString(StringBuffer bld, Variable rootVariable, Expression[] elements) {
+ int top = elements.length;
+ if (top > 0) {
+ elements[0].toString(bld, rootVariable);
+ for (int idx = 1; idx < top; ++idx) {
+ bld.append(", "); //$NON-NLS-1$
+ appendOperand(bld, rootVariable, elements[idx], PRIORITY_MAX);
+ }
+ }
+ }
+
+ /**
+ * Let the visitor visit this instance and all expressions that this
+ * instance contains.
+ * @param visitor The visiting visitor.
+ * @return <code>true</code> if the visitor should continue visiting, <code>false</code> otherwise.
+ */
+ public boolean accept(IExpressionVisitor visitor) {
+ return visitor.visit(this);
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = getPriority() - e.getPriority();
+ if (cmp == 0) {
+ int e1 = getExpressionType();
+ int e2 = e.getExpressionType();
+ cmp = e1 > e2 ? 1 : (e1 == e2 ? 0 : -1);
+ }
+ return cmp;
+ }
+
+ public boolean equals(Object e) {
+ if (e == this)
+ return true;
+ if (e == null || getClass() != e.getClass())
+ return false;
+ return getExpressionType() == ((Expression) e).getExpressionType();
+ }
+
+ /**
+ * Evaluate this expression with given context and variables.
+ * @param context The evaluation context
+ * @return The result of the evaluation.
+ */
+ public abstract Object evaluate(IEvaluationContext context);
+
+ public Iterator<?> evaluateAsIterator(IEvaluationContext context) {
+ Object value = evaluate(context);
+ if (!(value instanceof Iterator<?>))
+ value = RepeatableIterator.create(value);
+ return (Iterator<?>) value;
+ }
+
+ public abstract String getOperator();
+
+ public abstract int getPriority();
+
+ public boolean isRootVariable() {
+ return false;
+ }
+
+ public final String toLDAPString() {
+ StringBuffer bld = new StringBuffer();
+ toLDAPString(bld);
+ return bld.toString();
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ throw new UnsupportedOperationException();
+ }
+
+ public final String toString() {
+ StringBuffer bld = new StringBuffer();
+ toString(bld);
+ return bld.toString();
+ }
+
+ public void toString(StringBuffer bld) {
+ toString(bld, ExpressionFactory.THIS);
+ }
+
+ public abstract void toString(StringBuffer bld, Variable rootVariable);
+
+ private static class Compacter {
+ private Expression base;
+
+ private List<Expression> parts;
+
+ private int op;
+
+ Compacter(Expression base, int op) {
+ this.base = base;
+ this.op = op;
+ }
+
+ Expression getResultingFilter() {
+ if (parts == null)
+ return base;
+
+ int partsOp = op == TYPE_AND ? TYPE_OR : TYPE_AND;
+ return addFilter(base, normalize(parts, partsOp), op);
+ }
+
+ boolean merge(Expression b) {
+ Expression[] aArr;
+ Expression[] bArr;
+ if (base.getExpressionType() == op)
+ aArr = getFilterImpls(base);
+ else
+ aArr = new Expression[] {base};
+
+ if (b.getExpressionType() == op)
+ bArr = getFilterImpls(b);
+ else
+ bArr = new Expression[] {b};
+
+ List<Expression> common = null;
+ List<Expression> onlyA = null;
+
+ int atop = aArr.length;
+ int btop = bArr.length;
+ int aidx;
+ int bidx;
+ for (aidx = 0; aidx < atop; ++aidx) {
+ Expression af = aArr[aidx];
+ for (bidx = 0; bidx < btop; ++bidx) {
+ Expression bf = bArr[bidx];
+ if (af.equals(bf)) {
+ if (common == null)
+ common = new ArrayList<Expression>();
+ common.add(af);
+ break;
+ }
+ }
+ if (bidx == btop) {
+ if (onlyA == null)
+ onlyA = new ArrayList<Expression>();
+ onlyA.add(af);
+ }
+ }
+ if (common == null)
+ // Nothing in common
+ return false;
+
+ if (onlyA == null && parts == null)
+ return true;
+
+ List<Expression> onlyB = null;
+ for (bidx = 0; bidx < btop; ++bidx) {
+ Expression bf = bArr[bidx];
+ for (aidx = 0; aidx < atop; ++aidx)
+ if (bf.equals(aArr[aidx]))
+ break;
+ if (aidx == atop) {
+ if (onlyB == null)
+ onlyB = new ArrayList<Expression>();
+ onlyB.add(bf);
+ }
+ }
+
+ if (onlyB == null && parts == null) {
+ // All of B is already covered by base
+ base = b;
+ return true;
+ }
+
+ if (parts == null)
+ parts = new ArrayList<Expression>();
+
+ if (onlyA != null) {
+ base = normalize(common, op);
+ Expression af = normalize(onlyA, op);
+ if (!parts.contains(af))
+ parts.add(af);
+ }
+ Expression bf = normalize(onlyB, op);
+ if (!parts.contains(bf))
+ parts.add(bf);
+ return true;
+ }
+ }
+
+ static Expression addFilter(Expression base, Expression subFilter, int expressionType) {
+ if (base.equals(subFilter))
+ return base;
+
+ ArrayList<Expression> filters = new ArrayList<Expression>(2);
+ filters.add(base);
+ filters.add(subFilter);
+ return normalize(filters, expressionType);
+ }
+
+ static Expression normalize(List<Expression> operands, int op) {
+ int top = operands.size();
+ if (top == 1)
+ return operands.get(0);
+
+ // a | (b | c) becomes a | b | c
+ // a & (b & c) becomes a & b & c
+ //
+ for (int idx = 0; idx < top; ++idx) {
+ Expression f = operands.get(idx);
+ if (f.getExpressionType() != op)
+ continue;
+
+ Expression[] sfs = getFilterImpls(f);
+ operands.remove(idx);
+ --top;
+ for (int ndx = 0; ndx < sfs.length; ++ndx) {
+ Expression nf = sfs[ndx];
+ if (!operands.contains(nf))
+ operands.add(nf);
+ }
+ }
+ top = operands.size();
+ if (top == 1)
+ return operands.get(0);
+
+ Collections.sort(operands);
+ List<Compacter> splits = new ArrayList<Compacter>();
+ int reverseOp = op == TYPE_AND ? TYPE_OR : TYPE_AND;
+
+ for (int idx = 0; idx < top; ++idx)
+ merge(splits, operands.get(idx), reverseOp);
+
+ operands.clear();
+ top = splits.size();
+ for (int idx = 0; idx < top; ++idx) {
+ Expression filter = splits.get(idx).getResultingFilter();
+ if (!operands.contains(filter))
+ operands.add(filter);
+ }
+ top = operands.size();
+ if (top == 1)
+ return operands.get(0);
+
+ Collections.sort(operands);
+ Expression[] expArray = operands.toArray(new Expression[top]);
+ return op == TYPE_AND ? new And(expArray) : new Or(expArray);
+ }
+
+ static void merge(List<Compacter> splits, Expression base, int op) {
+ int top = splits.size();
+ for (int idx = 0; idx < top; ++idx) {
+ Compacter split = splits.get(idx);
+ if (split.merge(base))
+ return;
+ }
+ splits.add(new Compacter(base, op));
+ }
+
+ static Expression[] getFilterImpls(Expression expression) {
+ if (expression instanceof NAry)
+ return ((NAry) expression).operands;
+ throw new IllegalArgumentException();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/ExpressionFactory.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/ExpressionFactory.java
new file mode 100644
index 000000000..a9e70e311
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/ExpressionFactory.java
@@ -0,0 +1,125 @@
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.List;
+import org.eclipse.equinox.p2.metadata.expression.*;
+
+public class ExpressionFactory implements IExpressionFactory, IExpressionConstants {
+ public static final IExpressionFactory INSTANCE = new ExpressionFactory();
+ public static final Variable THIS = new Variable(VARIABLE_THIS);
+
+ protected static Expression[] convertArray(IExpression[] operands) {
+ Expression[] ops = new Expression[operands.length];
+ System.arraycopy(operands, 0, ops, 0, operands.length);
+ return ops;
+ }
+
+ protected ExpressionFactory() {
+ // Maintain singleton
+ }
+
+ public IExpression all(IExpression collection, IExpression lambda) {
+ return new All((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression and(IExpression... operands) {
+ return new And(convertArray(operands));
+ }
+
+ public IExpression at(IExpression target, IExpression key) {
+ return new At((Expression) target, (Expression) key);
+ }
+
+ @SuppressWarnings("unchecked")
+ public IExpression normalize(List<? extends IExpression> operands, int expressionType) {
+ return Expression.normalize((List<Expression>) operands, expressionType);
+ }
+
+ public IExpression constant(Object value) {
+ return Literal.create(value);
+ }
+
+ public IEvaluationContext createContext(Object... parameters) {
+ return EvaluationContext.create(parameters, (Variable[]) null);
+ }
+
+ public IEvaluationContext createContext(IExpression[] variables, Object... parameters) {
+ return EvaluationContext.create(parameters, variables);
+ }
+
+ public IFilterExpression filterExpression(IExpression expression) {
+ return new LDAPFilter((Expression) expression);
+ }
+
+ public IExpression equals(IExpression lhs, IExpression rhs) {
+ return new Equals((Expression) lhs, (Expression) rhs, false);
+ }
+
+ public IExpression exists(IExpression collection, IExpression lambda) {
+ return new Exists((Expression) collection, (LambdaExpression) lambda);
+ }
+
+ public IExpression greater(IExpression lhs, IExpression rhs) {
+ return new Compare((Expression) lhs, (Expression) rhs, false, false);
+ }
+
+ public IExpression greaterEqual(IExpression lhs, IExpression rhs) {
+ return new Compare((Expression) lhs, (Expression) rhs, false, true);
+ }
+
+ public IExpression indexedParameter(int index) {
+ return new Parameter(index);
+ }
+
+ public IExpression lambda(IExpression variable, IExpression body) {
+ return new LambdaExpression((Variable) variable, (Expression) body);
+ }
+
+ public IExpression less(IExpression lhs, IExpression rhs) {
+ return new Compare((Expression) lhs, (Expression) rhs, true, false);
+ }
+
+ public IExpression lessEqual(IExpression lhs, IExpression rhs) {
+ return new Compare((Expression) lhs, (Expression) rhs, true, true);
+ }
+
+ public IExpression matches(IExpression lhs, IExpression rhs) {
+ return new Matches((Expression) lhs, (Expression) rhs);
+ }
+
+ public <T> IMatchExpression<T> matchExpression(IExpression expression, Object... parameters) {
+ return new MatchExpression<T>((Expression) expression, parameters);
+ }
+
+ public IExpression member(IExpression target, String name) {
+ return new Member.DynamicMember((Expression) target, name);
+ }
+
+ public IExpression not(IExpression operand) {
+ if (operand instanceof Equals) {
+ Equals eq = (Equals) operand;
+ return new Equals(eq.lhs, eq.rhs, !eq.negate);
+ }
+ if (operand instanceof Compare) {
+ Compare cmp = (Compare) operand;
+ return new Compare(cmp.lhs, cmp.rhs, !cmp.compareLess, !cmp.equalOK);
+ }
+ if (operand instanceof Not)
+ return ((Not) operand).operand;
+
+ return new Not((Expression) operand);
+ }
+
+ public IExpression or(IExpression... operands) {
+ return new Or(convertArray(operands));
+ }
+
+ public IExpression thisVariable() {
+ return THIS;
+ }
+
+ public IExpression variable(String name) {
+ if (VARIABLE_THIS.equals(name))
+ return THIS;
+ return new Variable(name);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IExpressionConstants.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IExpressionConstants.java
new file mode 100644
index 000000000..3da1dbfac
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IExpressionConstants.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+public interface IExpressionConstants {
+ String KEYWORD_ALL = "all"; //$NON-NLS-1$
+
+ String KEYWORD_BOOLEAN = "boolean"; //$NON-NLS-1$
+ String KEYWORD_CLASS = "class"; //$NON-NLS-1$
+ String KEYWORD_EXISTS = "exists"; //$NON-NLS-1$
+ String KEYWORD_FALSE = "false"; //$NON-NLS-1$
+ String KEYWORD_FILTER = "filter"; //$NON-NLS-1$
+ String KEYWORD_NULL = "null"; //$NON-NLS-1$
+ String KEYWORD_RANGE = "range"; //$NON-NLS-1$
+ String KEYWORD_TRUE = "true"; //$NON-NLS-1$
+
+ String KEYWORD_VERSION = "version"; //$NON-NLS-1$
+ String OPERATOR_AND = "&&"; //$NON-NLS-1$
+ String OPERATOR_AT = "[]"; //$NON-NLS-1$
+ String OPERATOR_EQUALS = "=="; //$NON-NLS-1$
+ String OPERATOR_GT = ">"; //$NON-NLS-1$
+ String OPERATOR_GT_EQUAL = ">="; //$NON-NLS-1$
+ String OPERATOR_LT = "<"; //$NON-NLS-1$
+ String OPERATOR_LT_EQUAL = "<="; //$NON-NLS-1$
+ String OPERATOR_MATCHES = "~="; //$NON-NLS-1$
+ String OPERATOR_MEMBER = "."; //$NON-NLS-1$
+ String OPERATOR_NOT = "!"; //$NON-NLS-1$
+ String OPERATOR_NOT_EQUALS = "!="; //$NON-NLS-1$
+
+ String OPERATOR_OR = "||"; //$NON-NLS-1$
+ String OPERATOR_PARAMETER = "$"; //$NON-NLS-1$
+
+ int PRIORITY_LITERAL = 1;
+ int PRIORITY_VARIABLE = 1;
+ int PRIORITY_FUNCTION = 2; // for extend query expressions
+ int PRIORITY_MEMBER = 3;
+ int PRIORITY_COLLECTION = 4;
+ int PRIORITY_NOT = 5;
+ int PRIORITY_BINARY = 6;
+ int PRIORITY_AND = 7;
+ int PRIORITY_OR = 8;
+ int PRIORITY_CONDITION = 9;
+ int PRIORITY_ASSIGNMENT = 10;
+ int PRIORITY_LAMBDA = 11;
+ int PRIORITY_COMMA = 12;
+ int PRIORITY_MAX = 20;
+
+ String VARIABLE_EVERYTHING = "everything"; //$NON-NLS-1$
+ String VARIABLE_THIS = "this"; //$NON-NLS-1$
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IRepeatableIterator.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IRepeatableIterator.java
new file mode 100644
index 000000000..f20e266bf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/IRepeatableIterator.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Iterator;
+
+public interface IRepeatableIterator<T> extends Iterator<T> {
+ /**
+ * Returns a copy that will iterate over the same elements
+ * as this iterator. The contents or position of this iterator
+ * is left unchanged.
+ * @return A re-initialized copy of this iterator.
+ */
+ IRepeatableIterator<T> getCopy();
+
+ Object getIteratorProvider();
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPApproximation.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPApproximation.java
new file mode 100644
index 000000000..291a6ef6c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPApproximation.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.io.Serializable;
+
+/**
+ * Map a string for an LDAP APPROX (~=) comparison.
+ * This implementation removes white spaces and transforms everything to lower case.
+ */
+public final class LDAPApproximation implements Serializable, Comparable<LDAPApproximation> {
+ private static final long serialVersionUID = 4782295637798543587L;
+ private final String pattern;
+ private transient String approxPattern;
+
+ public LDAPApproximation(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public int compareTo(LDAPApproximation o) {
+ return pattern.compareTo(o.pattern);
+ }
+
+ public boolean equals(Object o) {
+ return o == this || (o instanceof LDAPApproximation && ((LDAPApproximation) o).pattern.equals(pattern));
+ }
+
+ public int hashCode() {
+ return 3 * pattern.hashCode();
+ }
+
+ /**
+ * Matches the <code>value</code> with the compiled expression. The value
+ * is considered matching if all characters are matched by the expression. A
+ * partial match is not enough.
+ * @param value The value to match
+ * @return <code>true</code> if the value was a match.
+ */
+ public boolean isMatch(CharSequence value) {
+ if (value == null)
+ return false;
+ if (approxPattern == null)
+ approxPattern = approxString(pattern);
+ return approxString(value).equals(approxPattern);
+ }
+
+ public String toString() {
+ return pattern;
+ }
+
+ private static String approxString(CharSequence input) {
+ boolean changed = false;
+ char[] output = new char[input.length()];
+ int cursor = 0;
+ for (int i = 0, length = output.length; i < length; i++) {
+ char c = input.charAt(i);
+ if (Character.isWhitespace(c)) {
+ changed = true;
+ continue;
+ }
+ if (Character.isUpperCase(c)) {
+ changed = true;
+ c = Character.toLowerCase(c);
+ }
+ output[cursor++] = c;
+ }
+ return changed ? new String(output, 0, cursor) : input.toString();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPFilter.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPFilter.java
new file mode 100644
index 000000000..607334bea
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LDAPFilter.java
@@ -0,0 +1,69 @@
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Dictionary;
+import java.util.Map;
+import org.eclipse.equinox.p2.metadata.expression.*;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceReference;
+
+public class LDAPFilter extends Unary implements IFilterExpression {
+
+ LDAPFilter(Expression expression) {
+ super(expression);
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return operand.accept(visitor);
+ }
+
+ public boolean equals(Object o) {
+ return (o instanceof Filter && !(o instanceof LDAPFilter)) ? equals(ExpressionUtil.parseLDAP(o.toString())) : super.equals(o);
+ }
+
+ @Override
+ public String getOperator() {
+ return operand.getOperator();
+ }
+
+ @Override
+ public int getPriority() {
+ return operand.getPriority();
+ }
+
+ public int getExpressionType() {
+ return 0;
+ }
+
+ public boolean match(Map<String, ? extends Object> map) {
+ return isMatch(MemberProvider.create(map, true));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public boolean match(Dictionary dictionary) {
+ return isMatch(dictionary == null ? MemberProvider.emptyProvider() : MemberProvider.create(dictionary, true));
+ }
+
+ private boolean isMatch(Object candidate) {
+ Variable self = ExpressionFactory.THIS;
+ IEvaluationContext ctx = EvaluationContext.create(self);
+ self.setValue(ctx, candidate);
+ return Boolean.TRUE == operand.evaluate(ctx);
+ }
+
+ public boolean match(ServiceReference reference) {
+ return isMatch(reference == null ? MemberProvider.emptyProvider() : MemberProvider.create(reference, true));
+ }
+
+ public boolean matchCase(Map<String, ? extends Object> map) {
+ return isMatch(map == null ? MemberProvider.emptyProvider() : MemberProvider.create(map, false));
+ }
+
+ @SuppressWarnings("rawtypes")
+ public boolean matchCase(Dictionary dictionary) {
+ return isMatch(dictionary == null ? MemberProvider.emptyProvider() : MemberProvider.create(dictionary, false));
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ operand.toLDAPString(bld);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LambdaExpression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LambdaExpression.java
new file mode 100644
index 000000000..c5bacf0ab
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/LambdaExpression.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+import org.eclipse.equinox.p2.metadata.expression.IExpressionVisitor;
+
+/**
+ * A function that executes some code
+ */
+public class LambdaExpression extends Unary {
+ protected final Variable each;
+
+ protected LambdaExpression(Variable each, Expression body) {
+ super(body);
+ this.each = each;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && each.accept(visitor);
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0)
+ cmp = each.compareTo(((LambdaExpression) e).each);
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ return super.equals(o) && each.equals(((LambdaExpression) o).each);
+ }
+
+ public int hashCode() {
+ int result = 31 + operand.hashCode();
+ return 31 * result + each.hashCode();
+ }
+
+ public int getExpressionType() {
+ return TYPE_LAMBDA;
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ each.toString(bld, rootVariable);
+ bld.append(" | "); //$NON-NLS-1$
+ appendOperand(bld, rootVariable, operand, IExpressionConstants.PRIORITY_COMMA);
+ }
+
+ public Variable getItemVariable() {
+ return each;
+ }
+
+ public String getOperator() {
+ return "|"; //$NON-NLS-1$
+ }
+
+ public int getPriority() {
+ return IExpressionConstants.PRIORITY_LAMBDA;
+ }
+
+ public IEvaluationContext prolog(IEvaluationContext context) {
+ return EvaluationContext.create(context, each);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Literal.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Literal.java
new file mode 100644
index 000000000..2884fb757
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Literal.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+import org.eclipse.equinox.p2.metadata.expression.SimplePattern;
+import org.osgi.framework.Filter;
+
+/**
+ * An expression that represents a constant value.
+ */
+public final class Literal extends Expression {
+ public static final Literal FALSE_CONSTANT = new Literal(Boolean.FALSE);
+
+ public static final Literal NULL_CONSTANT = new Literal(null);
+
+ public static final Literal TRUE_CONSTANT = new Literal(Boolean.TRUE);
+
+ static Literal create(Object value) {
+ if (value == null)
+ return NULL_CONSTANT;
+ if (value == Boolean.TRUE)
+ return TRUE_CONSTANT;
+ if (value == Boolean.FALSE)
+ return FALSE_CONSTANT;
+ return new Literal(value);
+ }
+
+ public final Object value;
+
+ private Literal(Object value) {
+ this.value = value;
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp != 0)
+ return cmp;
+
+ Object eValue = ((Literal) e).value;
+ if (value == null)
+ return eValue == null ? 0 : -1;
+
+ if (eValue == null)
+ return 1;
+
+ if (eValue.getClass() == value.getClass())
+ return ((Comparable) value).compareTo(eValue);
+
+ return eValue.getClass().getName().compareTo(value.getClass().getName());
+ }
+
+ public boolean equals(Object o) {
+ if (super.equals(o)) {
+ Literal bo = (Literal) o;
+ return value == null ? bo.value == null : value.equals(bo.value);
+ }
+ return false;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return value;
+ }
+
+ public int getExpressionType() {
+ return TYPE_LITERAL;
+ }
+
+ public String getOperator() {
+ return "<literal>"; //$NON-NLS-1$
+ }
+
+ public int getPriority() {
+ return PRIORITY_LITERAL;
+ }
+
+ public int hashCode() {
+ return 31 + value.hashCode();
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ if (!(value instanceof Filter))
+ throw new UnsupportedOperationException();
+ buf.append(value.toString());
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ if (value == null)
+ bld.append("null"); //$NON-NLS-1$
+ else if (value instanceof String || value instanceof Version) {
+ String str = value.toString();
+ char sep = str.indexOf('\'') >= 0 ? '"' : '\'';
+ bld.append(sep);
+ bld.append(str);
+ bld.append(sep);
+ } else if (value instanceof SimplePattern) {
+ appendEscaped(bld, '/', value.toString());
+ } else
+ bld.append(value);
+ }
+
+ private void appendEscaped(StringBuffer bld, char delimiter, String str) {
+ bld.append(delimiter);
+ int top = str.length();
+ for (int idx = 0; idx < top; ++idx) {
+ char c = str.charAt(idx);
+ if (c == delimiter)
+ bld.append('\\');
+ bld.append(c);
+ }
+ bld.append(delimiter);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MatchExpression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MatchExpression.java
new file mode 100644
index 000000000..efb636bb9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MatchExpression.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Arrays;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
+import org.eclipse.equinox.p2.metadata.expression.*;
+
+/**
+ * The MatchExpression is a wrapper for an {@link IExpression} that is expected
+ * to return a boolean value. The wrapper provides the evaluation context needed
+ * to evaluate the expression.
+ */
+class MatchExpression<T> extends Unary implements IMatchExpression<T> {
+ private static final Object[] noParams = new Object[0];
+ private final Object[] parameters;
+
+ MatchExpression(Expression expression, Object[] parameters) {
+ super(expression);
+ this.parameters = parameters == null ? noParams : parameters;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return operand.accept(visitor);
+ }
+
+ public IEvaluationContext createContext() {
+ return EvaluationContext.create(parameters, ExpressionFactory.THIS);
+ }
+
+ public boolean equals(Object o) {
+ return super.equals(o) && Arrays.equals(parameters, ((MatchExpression<?>) o).parameters);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return operand.evaluate(parameters.length == 0 ? context : EvaluationContext.create(context, parameters));
+ }
+
+ public int getExpressionType() {
+ return 0;
+ }
+
+ public String getOperator() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object[] getParameters() {
+ return parameters;
+ }
+
+ public int getPriority() {
+ return operand.getPriority();
+ }
+
+ public int hashCode() {
+ return operand.hashCode() * 31 + CollectionUtils.hashCode(parameters);
+ }
+
+ public boolean isMatch(IEvaluationContext context, T value) {
+ ExpressionFactory.THIS.setValue(context, value);
+ return Boolean.TRUE == operand.evaluate(context);
+ }
+
+ public boolean isMatch(T value) {
+ return isMatch(createContext(), value);
+ }
+
+ public void toLDAPString(StringBuffer bld) {
+ operand.toLDAPString(bld);
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ operand.toString(bld, rootVariable);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Matches.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Matches.java
new file mode 100644
index 000000000..da041b398
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Matches.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.VersionRange;
+import org.eclipse.equinox.p2.metadata.expression.*;
+
+/**
+ * <p>A class that performs &quot;matching&quot; The actual algorithm used for
+ * performing the match varies depending on the types of the items to match.</p>
+ * <p>The following things can be matched:</p>
+ * <table border="1" cellpadding="3">
+ * <tr><th>LHS</th><th>RHS</th><th>Implemented as</th></tr>
+ * <tr><td>{@link String}</td><td>{@link SimplePattern}</td><td>rhs.isMatch(lhs)</td></tr>
+ * <tr><td>{@link String}</td><td>{@link LDAPApproximation}</td><td>rhs.isMatch(lhs)</td></tr>
+ * <tr><td>&lt;any&gt;</td><td>{@link Class}</td><td>rhs.isInstance(lhs)</td></tr>
+ * <tr><td>{@link Class}</td><td>{@link Class}</td><td>rhs.isAssignableFrom(lhs)</td></tr>
+ * </table>
+ */
+public class Matches extends Binary {
+ protected Matches(Expression lhs, Expression rhs) {
+ super(lhs, rhs);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return Boolean.valueOf(match(lhs.evaluate(context), rhs.evaluate(context)));
+ }
+
+ protected boolean match(Object lval, Object rval) {
+ if (rval instanceof VersionRange) {
+ VersionRange range = (VersionRange) rval;
+ if (lval instanceof Version)
+ return range.isIncluded((Version) lval);
+ if (lval instanceof String)
+ return range.isIncluded(Version.create((String) lval));
+ }
+ if (rval instanceof SimplePattern) {
+ if (lval instanceof CharSequence)
+ return ((SimplePattern) rval).isMatch((CharSequence) lval);
+ if (lval instanceof Character || lval instanceof Number || lval instanceof Boolean)
+ return ((SimplePattern) rval).isMatch(lval.toString());
+
+ } else if (rval instanceof LDAPApproximation) {
+ if (lval instanceof CharSequence)
+ return ((LDAPApproximation) rval).isMatch((CharSequence) lval);
+ if (lval instanceof Character || lval instanceof Number || lval instanceof Boolean)
+ return ((LDAPApproximation) rval).isMatch(lval.toString());
+
+ } else if (rval instanceof Class<?>) {
+ Class<?> rclass = (Class<?>) rval;
+ return lval instanceof Class<?> ? rclass.isAssignableFrom((Class<?>) lval) : rclass.isInstance(lval);
+ }
+
+ if (lval == null || rval == null)
+ return false;
+
+ throw new IllegalArgumentException("Cannot match a " + lval.getClass().getName() + " with a " + rval.getClass().getName()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+
+ public int getExpressionType() {
+ return TYPE_MATCHES;
+ }
+
+ public String getOperator() {
+ return OPERATOR_MATCHES;
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ if (!(rhs instanceof Literal))
+ throw new UnsupportedOperationException();
+
+ boolean escapeWild = true;
+ Object val = rhs.evaluate(null);
+ buf.append('(');
+ appendLDAPAttribute(buf);
+ if (val instanceof LDAPApproximation) {
+ buf.append(getOperator());
+ } else if (val instanceof SimplePattern) {
+ buf.append('=');
+ escapeWild = false;
+ } else
+ throw new UnsupportedOperationException();
+ appendLDAPEscaped(buf, val.toString(), escapeWild);
+ buf.append(')');
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Member.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Member.java
new file mode 100644
index 000000000..5d442f62c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Member.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.lang.reflect.*;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+import org.eclipse.equinox.p2.metadata.expression.IExpressionVisitor;
+
+/**
+ * <p>An expression that performs member calls to obtain some value
+ * from some object instance. It uses standard bean semantics so
+ * that an attempt to obtain &quot;value&quot; will cause an
+ * attempt to call <code>getValue()</code> and if no such method
+ * exists, <code>isValue()</code> and if that doesn't work either,
+ * <code>value()</code>.</p>
+ */
+public abstract class Member extends Unary {
+
+ public static final class DynamicMember extends Member {
+ private static final String GET_PREFIX = "get"; //$NON-NLS-1$
+ private static final String IS_PREFIX = "is"; //$NON-NLS-1$
+ private static final Class<?>[] NO_ARG_TYPES = new Class[0];
+
+ private Class<?> lastClass;
+
+ private Method method;
+ private String methodName;
+
+ DynamicMember(Expression operand, String name) {
+ super(operand, name, Expression.emptyArray);
+ if (!(name.startsWith(GET_PREFIX) || name.startsWith(IS_PREFIX)))
+ name = GET_PREFIX + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ this.methodName = name;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return invoke(operand.evaluate(context));
+ }
+
+ public Object invoke(Object self) {
+ if (self == null)
+ throw new IllegalArgumentException("Cannot access member \'" + name + "\' in null"); //$NON-NLS-1$//$NON-NLS-2$
+
+ if (self instanceof MemberProvider)
+ return ((MemberProvider) self).getMember(name);
+
+ Class<?> c = self.getClass();
+ if (lastClass == null || !lastClass.isAssignableFrom(c)) {
+ Method m;
+ for (;;) {
+ try {
+ m = c.getMethod(methodName, NO_ARG_TYPES);
+ if (!Modifier.isPublic(m.getModifiers()))
+ throw new NoSuchMethodException();
+ break;
+ } catch (NoSuchMethodException e) {
+ if (methodName.startsWith(GET_PREFIX))
+ // Switch from using getXxx() to isXxx()
+ methodName = IS_PREFIX + Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ else if (methodName.startsWith(IS_PREFIX))
+ // Switch from using isXxx() to xxx()
+ methodName = name;
+ else
+ throw new IllegalArgumentException("Cannot find a public member \'" + name + "\' in a " + self.getClass().getName()); //$NON-NLS-1$//$NON-NLS-2$
+ }
+ }
+
+ // Since we already checked that it's public. This will speed
+ // up the calls a bit.
+ m.setAccessible(true);
+ lastClass = c;
+ method = m;
+ }
+
+ Exception checked;
+ try {
+ return method.invoke(self, NO_ARGS);
+ } catch (IllegalArgumentException e) {
+ throw e;
+ } catch (IllegalAccessException e) {
+ checked = e;
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getTargetException();
+ if (cause instanceof RuntimeException)
+ throw (RuntimeException) cause;
+ if (cause instanceof Error)
+ throw (Error) cause;
+ checked = (Exception) cause;
+ }
+ throw new RuntimeException("Problem invoking " + methodName + " on a " + self.getClass().getName(), checked); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+
+ static final Object[] NO_ARGS = new Object[0];
+
+ static Member createDynamicMember(Expression operand, String name) {
+ return new DynamicMember(operand, name);
+ }
+
+ protected final Expression[] argExpressions;
+
+ final String name;
+
+ protected Member(Expression operand, String name, Expression[] args) {
+ super(operand);
+ this.name = name;
+ this.argExpressions = args;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ if (super.accept(visitor))
+ for (int idx = 0; idx < argExpressions.length; ++idx)
+ if (!argExpressions[idx].accept(visitor))
+ return false;
+ return true;
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0) {
+ cmp = name.compareTo(((Member) e).name);
+ if (cmp == 0)
+ cmp = compare(argExpressions, ((Member) e).argExpressions);
+ }
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ if (super.equals(o)) {
+ Member mo = (Member) o;
+ return name.equals(mo.name) && equals(argExpressions, mo.argExpressions);
+ }
+ return false;
+ }
+
+ public int getExpressionType() {
+ return TYPE_MEMBER;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getOperator() {
+ return OPERATOR_MEMBER;
+ }
+
+ public int getPriority() {
+ return PRIORITY_MEMBER;
+ }
+
+ public int hashCode() {
+ int result = 31 + name.hashCode();
+ result = 31 * result + operand.hashCode();
+ return 31 * result + hashCode(argExpressions);
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ if (operand != rootVariable) {
+ appendOperand(bld, rootVariable, operand, getPriority());
+ bld.append('.');
+ }
+ bld.append(name);
+ if (argExpressions.length > 0) {
+ bld.append('(');
+ elementsToString(bld, rootVariable, argExpressions);
+ bld.append(')');
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MemberProvider.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MemberProvider.java
new file mode 100644
index 000000000..9eb460a8d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/MemberProvider.java
@@ -0,0 +1,139 @@
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.*;
+import java.util.Map.Entry;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
+import org.osgi.framework.ServiceReference;
+
+public abstract class MemberProvider {
+
+ static class DictionaryMemberProvider extends MemberProvider {
+ private final Dictionary<String, ? extends Object> dictionary;
+
+ public DictionaryMemberProvider(Dictionary<String, ? extends Object> dictionary) {
+ this.dictionary = dictionary;
+ }
+
+ @Override
+ public Object getMember(String memberName) {
+ return dictionary.get(memberName);
+ }
+ }
+
+ static class CIDictionaryMemberProvider extends DictionaryMemberProvider {
+ public CIDictionaryMemberProvider(Dictionary<String, ? extends Object> dictionary) {
+ super(lowerCaseKeys(dictionary));
+ }
+
+ @Override
+ public Object getMember(String memberName) {
+ return super.getMember(memberName == null ? null : memberName.toLowerCase());
+ }
+
+ private static Dictionary<String, ? extends Object> lowerCaseKeys(Dictionary<String, ? extends Object> dictionary) {
+ boolean hasUpperCase = false;
+ for (Enumeration<String> keys = dictionary.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ if (key.toLowerCase() != key) {
+ hasUpperCase = true;
+ break;
+ }
+ }
+ if (!hasUpperCase)
+ return dictionary;
+
+ Dictionary<String, Object> lcMap = new Hashtable<String, Object>(dictionary.size());
+ for (Enumeration<String> keys = dictionary.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ if (lcMap.put(key.toLowerCase(), dictionary.get(key)) != null)
+ throw new IllegalArgumentException("case variants of the same key name: '" + key + '\''); //$NON-NLS-1$
+ }
+ return lcMap;
+ }
+ }
+
+ static class MapMemberProvider extends MemberProvider {
+ private final Map<String, ? extends Object> map;
+
+ public MapMemberProvider(Map<String, ? extends Object> map) {
+ this.map = map;
+ }
+
+ @Override
+ public Object getMember(String memberName) {
+ return map.get(memberName);
+ }
+ }
+
+ static class CIMapMemberProvider extends MapMemberProvider {
+ public CIMapMemberProvider(Map<String, ? extends Object> map) {
+ super(lowerCaseKeys(map));
+ }
+
+ @Override
+ public Object getMember(String memberName) {
+ return super.getMember(memberName == null ? null : memberName.toLowerCase());
+ }
+
+ private static Map<String, ? extends Object> lowerCaseKeys(Map<String, ? extends Object> map) {
+ boolean hasUpperCase = false;
+ Set<? extends Entry<String, ? extends Object>> entrySet = map.entrySet();
+ for (Entry<String, ?> entry : entrySet) {
+ String key = entry.getKey();
+ String lowKey = key.toLowerCase();
+ if (key != lowKey) {
+ hasUpperCase = true;
+ break;
+ }
+ }
+ if (!hasUpperCase)
+ return map;
+
+ Map<String, Object> lcMap = new HashMap<String, Object>(map.size());
+ for (Entry<String, ?> entry : entrySet) {
+ if (lcMap.put(entry.getKey().toLowerCase(), entry.getValue()) != null)
+ throw new IllegalArgumentException("case variants of the same key name: '" + entry.getKey() + '\''); //$NON-NLS-1$
+ }
+ return lcMap;
+ }
+ }
+
+ static class ServiceRefMemberProvider extends MemberProvider {
+ private final ServiceReference serviceRef;
+
+ public ServiceRefMemberProvider(ServiceReference serviceRef) {
+ this.serviceRef = serviceRef;
+ }
+
+ @Override
+ public Object getMember(String memberName) {
+ return serviceRef.getProperty(memberName);
+ }
+ }
+
+ private static final MemberProvider emptyProvider = create(CollectionUtils.emptyMap(), false);
+
+ /**
+ * Create a new member provider on the given value. The value can be an instance of a {@link Map}, {@link Dictionary},
+ * or {@link ServiceReference}.
+ * @param value The value that provides the members
+ * @param caseInsensitive <code>true</code> if the members should be retrievable in a case insensitive way.
+ * @return A member provided that is backed by <code>value</code>.
+ */
+ @SuppressWarnings("unchecked")
+ public static MemberProvider create(Object value, boolean caseInsensitive) {
+ if (value instanceof Map<?, ?>)
+ return caseInsensitive ? new CIMapMemberProvider((Map<String, ?>) value) : new MapMemberProvider((Map<String, ?>) value);
+ if (value instanceof Dictionary<?, ?>)
+ return caseInsensitive ? new CIDictionaryMemberProvider((Dictionary<String, ?>) value) : new DictionaryMemberProvider((Dictionary<String, ?>) value);
+ if (value instanceof ServiceReference)
+ return new ServiceRefMemberProvider((ServiceReference) value);
+ throw new IllegalArgumentException();
+ }
+
+ public abstract Object getMember(String memberName);
+
+ public static MemberProvider emptyProvider() {
+ return emptyProvider;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/NAry.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/NAry.java
new file mode 100644
index 000000000..805cff2ab
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/NAry.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IExpressionVisitor;
+
+/**
+ * The abstract baseclass for all N-ary expressions
+ */
+public abstract class NAry extends Expression {
+ public final Expression[] operands;
+
+ protected NAry(Expression[] operands) {
+ this.operands = operands;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ if (super.accept(visitor))
+ for (int idx = 0; idx < operands.length; ++idx)
+ if (!operands[idx].accept(visitor))
+ return false;
+ return true;
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0)
+ cmp = compare(operands, ((NAry) e).operands);
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ return super.equals(o) && equals(operands, ((NAry) o).operands);
+ }
+
+ public abstract String getOperator();
+
+ public int hashCode() {
+ return hashCode(operands);
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ appendOperand(bld, rootVariable, operands[0], getPriority());
+ for (int idx = 1; idx < operands.length; ++idx) {
+ bld.append(' ');
+ bld.append(getOperator());
+ bld.append(' ');
+ appendOperand(bld, rootVariable, operands[idx], getPriority());
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Not.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Not.java
new file mode 100644
index 000000000..5246859a3
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Not.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * An expression that yields <code>true</code> when its operand does not.
+ */
+final class Not extends Unary {
+ Not(Expression operand) {
+ super(operand);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return Boolean.valueOf(operand.evaluate(context) != Boolean.TRUE);
+ }
+
+ public int getExpressionType() {
+ return TYPE_NOT;
+ }
+
+ public String getOperator() {
+ return OPERATOR_NOT;
+ }
+
+ public int getPriority() {
+ return PRIORITY_NOT;
+ }
+
+ public int hashCode() {
+ return 3 * operand.hashCode();
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ buf.append("(!"); //$NON-NLS-1$
+ operand.toLDAPString(buf);
+ buf.append(')');
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Or.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Or.java
new file mode 100644
index 000000000..32805042e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Or.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * n-ary OR operator. The full evaluation is <code>false</code> if none of its operands
+ * evaluate to <code>true</code>.
+ */
+final class Or extends NAry {
+ public Or(Expression[] operands) {
+ super(assertLength(operands, 2, OPERATOR_OR));
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ for (int idx = 0; idx < operands.length; ++idx) {
+ if (operands[idx].evaluate(context) == Boolean.TRUE)
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ public int getExpressionType() {
+ return TYPE_OR;
+ }
+
+ public String getOperator() {
+ return OPERATOR_OR;
+ }
+
+ public int getPriority() {
+ return PRIORITY_OR;
+ }
+
+ public void toLDAPString(StringBuffer buf) {
+ buf.append("(|"); //$NON-NLS-1$
+ for (int idx = 0; idx < operands.length; ++idx)
+ operands[idx].toLDAPString(buf);
+ buf.append(')');
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Parameter.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Parameter.java
new file mode 100644
index 000000000..b4c56346e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Parameter.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * The abstract base class for the indexed and keyed parameters
+ */
+public class Parameter extends Expression {
+ final int position;
+
+ Parameter(int position) {
+ this.position = position;
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0)
+ cmp = position - ((Parameter) e).position;
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o == null)
+ return false;
+ return getClass() == o.getClass() && position == ((Parameter) o).position;
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return context.getParameter(position);
+ }
+
+ public int getExpressionType() {
+ return TYPE_PARAMETER;
+ }
+
+ public String getOperator() {
+ return OPERATOR_PARAMETER;
+ }
+
+ public int getPriority() {
+ return PRIORITY_VARIABLE;
+ }
+
+ public int hashCode() {
+ return 31 * (1 + position);
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ bld.append('$');
+ bld.append(position);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/RepeatableIterator.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/RepeatableIterator.java
new file mode 100644
index 000000000..e44a4e507
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/RepeatableIterator.java
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.*;
+import org.eclipse.equinox.p2.query.IQueryResult;
+
+public class RepeatableIterator<T> implements IRepeatableIterator<T> {
+ private final List<T> values;
+ private int position = -1;
+
+ @SuppressWarnings("unchecked")
+ public static <T> IRepeatableIterator<T> create(Object unknown) {
+ if (unknown.getClass().isArray())
+ return create((T[]) unknown);
+ if (unknown instanceof Iterator<?>)
+ return create((Iterator<T>) unknown);
+ if (unknown instanceof List<?>)
+ return create((List<T>) unknown);
+ if (unknown instanceof Collection<?>)
+ return create((Collection<T>) unknown);
+ if (unknown instanceof Map<?, ?>)
+ return create((Set<T>) ((Map<?, ?>) unknown).entrySet());
+ if (unknown instanceof IQueryResult<?>)
+ return create((IQueryResult<T>) unknown);
+ throw new IllegalArgumentException("Cannot convert a " + unknown.getClass().getName() + " into an iterator"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public static <T> IRepeatableIterator<T> create(Iterator<T> iterator) {
+ return iterator instanceof IRepeatableIterator<?> ? ((IRepeatableIterator<T>) iterator).getCopy() : new ElementRetainingIterator<T>(iterator);
+ }
+
+ public static <T> IRepeatableIterator<T> create(List<T> values) {
+ return new RepeatableIterator<T>(values);
+ }
+
+ public static <T> IRepeatableIterator<T> create(Collection<T> values) {
+ return new CollectionIterator<T>(values);
+ }
+
+ public static <T> IRepeatableIterator<T> create(IQueryResult<T> values) {
+ return new QueryResultIterator<T>(values);
+ }
+
+ public static <T> IRepeatableIterator<T> create(T[] values) {
+ return new ArrayIterator<T>(values);
+ }
+
+ RepeatableIterator(List<T> values) {
+ this.values = values;
+ }
+
+ public IRepeatableIterator<T> getCopy() {
+ return new RepeatableIterator<T>(values);
+ }
+
+ public boolean hasNext() {
+ return position + 1 < values.size();
+ }
+
+ public T next() {
+ if (++position == values.size()) {
+ --position;
+ throw new NoSuchElementException();
+ }
+ return values.get(position);
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object getIteratorProvider() {
+ return values;
+ }
+
+ void setPosition(int position) {
+ this.position = position;
+ }
+
+ List<T> getValues() {
+ return values;
+ }
+
+ static class ArrayIterator<T> implements IRepeatableIterator<T> {
+ private final T[] array;
+ private int position = -1;
+
+ public ArrayIterator(T[] array) {
+ this.array = array;
+ }
+
+ public Object getIteratorProvider() {
+ return array;
+ }
+
+ public boolean hasNext() {
+ return position + 1 < array.length;
+ }
+
+ public T next() {
+ if (++position >= array.length)
+ throw new NoSuchElementException();
+ return array[position];
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public IRepeatableIterator<T> getCopy() {
+ return new ArrayIterator<T>(array);
+ }
+ }
+
+ static class CollectionIterator<T> implements IRepeatableIterator<T> {
+ private final Collection<T> collection;
+
+ private final Iterator<T> iterator;
+
+ CollectionIterator(Collection<T> collection) {
+ this.collection = collection;
+ this.iterator = collection.iterator();
+ }
+
+ public IRepeatableIterator<T> getCopy() {
+ return new CollectionIterator<T>(collection);
+ }
+
+ public Object getIteratorProvider() {
+ return collection;
+ }
+
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ public T next() {
+ return iterator.next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class QueryResultIterator<T> implements IRepeatableIterator<T> {
+ private final IQueryResult<T> queryResult;
+
+ private final Iterator<T> iterator;
+
+ QueryResultIterator(IQueryResult<T> queryResult) {
+ this.queryResult = queryResult;
+ this.iterator = queryResult.iterator();
+ }
+
+ public IRepeatableIterator<T> getCopy() {
+ return new QueryResultIterator<T>(queryResult);
+ }
+
+ public Object getIteratorProvider() {
+ return queryResult;
+ }
+
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ public T next() {
+ return iterator.next();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static class ElementRetainingIterator<T> extends RepeatableIterator<T> {
+
+ private Iterator<T> innerIterator;
+
+ ElementRetainingIterator(Iterator<T> iterator) {
+ super(new ArrayList<T>());
+ innerIterator = iterator;
+ }
+
+ public synchronized boolean hasNext() {
+ if (innerIterator != null) {
+ if (innerIterator.hasNext())
+ return true;
+ innerIterator = null;
+ setPosition(getValues().size());
+ }
+ return super.hasNext();
+ }
+
+ public synchronized T next() {
+ if (innerIterator != null) {
+ T val = innerIterator.next();
+ getValues().add(val);
+ return val;
+ }
+ return super.next();
+ }
+
+ public synchronized IRepeatableIterator<T> getCopy() {
+ // If the current iterator still exists, we must exhaust it first
+ //
+ exhaustInnerIterator();
+ return super.getCopy();
+ }
+
+ public synchronized Object getIteratorProvider() {
+ exhaustInnerIterator();
+ return super.getIteratorProvider();
+ }
+
+ private void exhaustInnerIterator() {
+ if (innerIterator != null) {
+ List<T> values = getValues();
+ int savePos = values.size() - 1;
+ while (innerIterator.hasNext())
+ values.add(innerIterator.next());
+ innerIterator = null;
+ setPosition(savePos);
+ }
+ }
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Unary.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Unary.java
new file mode 100644
index 000000000..9c6b7d8a5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Unary.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+import org.eclipse.equinox.p2.metadata.expression.IExpressionVisitor;
+
+/**
+ * The abstract base class for all unary expressions
+ */
+public abstract class Unary extends Expression {
+ public final Expression operand;
+
+ protected Unary(Expression operand) {
+ this.operand = operand;
+ }
+
+ public boolean accept(IExpressionVisitor visitor) {
+ return super.accept(visitor) && operand.accept(visitor);
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0)
+ cmp = operand.compareTo(((Unary) e).operand);
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ return super.equals(o) && operand.equals(((Unary) o).operand);
+ }
+
+ public Object evaluate(IEvaluationContext context) {
+ return operand.evaluate(context);
+ }
+
+ public int hashCode() {
+ return operand.hashCode() * 3 + operand.getExpressionType();
+ }
+
+ public Expression getOperand() {
+ return operand;
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ bld.append(getOperator());
+ appendOperand(bld, rootVariable, operand, getPriority());
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Variable.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Variable.java
new file mode 100644
index 000000000..a7adfb738
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/Variable.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression;
+
+import java.util.Iterator;
+import org.eclipse.equinox.p2.metadata.expression.IEvaluationContext;
+
+/**
+ * An expression representing a variable stack in the current thread.
+ */
+public class Variable extends Expression {
+
+ private final String name;
+
+ public Variable(String name) {
+ this.name = name;
+ }
+
+ public int compareTo(Expression e) {
+ int cmp = super.compareTo(e);
+ if (cmp == 0)
+ cmp = name.compareTo(((Variable) e).name);
+ return cmp;
+ }
+
+ public boolean equals(Object o) {
+ return super.equals(o) && name.equals(((Variable) o).name);
+ }
+
+ public final Object evaluate(IEvaluationContext context) {
+ return context.getValue(this);
+ }
+
+ public Iterator<?> evaluateAsIterator(IEvaluationContext context) {
+ Object value = context.getValue(this);
+ if (value instanceof IRepeatableIterator<?>)
+ return ((IRepeatableIterator<?>) value).getCopy();
+
+ Iterator<?> itor = RepeatableIterator.create(value);
+ setValue(context, itor);
+ return itor;
+ }
+
+ public int getExpressionType() {
+ return TYPE_VARIABLE;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getOperator() {
+ return "<variable>"; //$NON-NLS-1$
+ }
+
+ public int getPriority() {
+ return PRIORITY_VARIABLE;
+ }
+
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ public final void setValue(IEvaluationContext context, Object value) {
+ context.setValue(this, value);
+ }
+
+ public void toString(StringBuffer bld, Variable rootVariable) {
+ bld.append(name);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/ExpressionParser.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/ExpressionParser.java
new file mode 100644
index 000000000..905d0927c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/ExpressionParser.java
@@ -0,0 +1,617 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression.parser;
+
+import org.eclipse.equinox.internal.p2.metadata.expression.LDAPApproximation;
+
+import java.util.*;
+import org.eclipse.equinox.internal.p2.metadata.expression.IExpressionConstants;
+import org.eclipse.equinox.p2.metadata.expression.*;
+
+public class ExpressionParser extends Stack<IExpression> implements IExpressionConstants, IExpressionParser {
+ private static final long serialVersionUID = 5481439062356612378L;
+
+ protected static final int TOKEN_OR = 1;
+ protected static final int TOKEN_AND = 2;
+
+ protected static final int TOKEN_EQUAL = 10;
+ protected static final int TOKEN_NOT_EQUAL = 11;
+ protected static final int TOKEN_LESS = 12;
+ protected static final int TOKEN_LESS_EQUAL = 13;
+ protected static final int TOKEN_GREATER = 14;
+ protected static final int TOKEN_GREATER_EQUAL = 15;
+ protected static final int TOKEN_MATCHES = 16;
+
+ protected static final int TOKEN_NOT = 20;
+ protected static final int TOKEN_DOT = 21;
+ protected static final int TOKEN_COMMA = 22;
+ protected static final int TOKEN_PIPE = 23;
+ protected static final int TOKEN_DOLLAR = 24;
+ protected static final int TOKEN_IF = 25;
+ protected static final int TOKEN_ELSE = 26;
+
+ protected static final int TOKEN_LP = 30;
+ protected static final int TOKEN_RP = 31;
+ protected static final int TOKEN_LB = 32;
+ protected static final int TOKEN_RB = 33;
+ protected static final int TOKEN_LC = 34;
+ protected static final int TOKEN_RC = 35;
+
+ protected static final int TOKEN_IDENTIFIER = 40;
+ protected static final int TOKEN_LITERAL = 41;
+
+ protected static final int TOKEN_NULL = 50;
+ protected static final int TOKEN_TRUE = 51;
+ protected static final int TOKEN_FALSE = 52;
+
+ private static final int TOKEN_ALL = 60;
+ private static final int TOKEN_EXISTS = 61;
+
+ protected static final int TOKEN_END = 0;
+ protected static final int TOKEN_ERROR = -1;
+
+ protected static final Map<String, Integer> keywords;
+ static {
+ keywords = new HashMap<String, Integer>();
+ keywords.put(KEYWORD_FALSE, new Integer(TOKEN_FALSE));
+ keywords.put(KEYWORD_NULL, new Integer(TOKEN_NULL));
+ keywords.put(KEYWORD_TRUE, new Integer(TOKEN_TRUE));
+ keywords.put(KEYWORD_ALL, new Integer(TOKEN_ALL));
+ keywords.put(KEYWORD_EXISTS, new Integer(TOKEN_EXISTS));
+ }
+
+ protected final IExpressionFactory factory;
+
+ protected String expression;
+ protected int tokenPos;
+ protected int currentToken;
+ protected int lastTokenPos;
+ protected Object tokenValue;
+ protected String rootVariable;
+
+ public ExpressionParser(IExpressionFactory factory) {
+ this.factory = factory;
+ }
+
+ public synchronized IExpression parse(String exprString) {
+ expression = exprString;
+ tokenPos = 0;
+ currentToken = 0;
+ tokenValue = null;
+ IExpression thisVariable = factory.thisVariable();
+ rootVariable = ExpressionUtil.getName(thisVariable);
+ push(thisVariable);
+ try {
+ nextToken();
+ IExpression expr = currentToken == TOKEN_END ? factory.constant(Boolean.TRUE) : parseCondition();
+ assertToken(TOKEN_END);
+ return expr;
+ } finally {
+ if (thisVariable != null)
+ popVariable(); // pop item
+ }
+ }
+
+ protected Map<String, Integer> keywordToTokenMap() {
+ return keywords;
+ }
+
+ protected IExpression parseCondition() {
+ // Just a hook in this parser. Conditions are not supported
+ return parseOr();
+ }
+
+ protected IExpression parseOr() {
+ IExpression expr = parseAnd();
+ if (currentToken != TOKEN_OR)
+ return expr;
+
+ ArrayList<IExpression> exprs = new ArrayList<IExpression>();
+ exprs.add(expr);
+ do {
+ nextToken();
+ exprs.add(parseAnd());
+ } while (currentToken == TOKEN_OR);
+ return factory.or(exprs.toArray(new IExpression[exprs.size()]));
+ }
+
+ protected IExpression parseAnd() {
+ IExpression expr = parseBinary();
+ if (currentToken != TOKEN_AND)
+ return expr;
+
+ ArrayList<IExpression> exprs = new ArrayList<IExpression>();
+ exprs.add(expr);
+ do {
+ nextToken();
+ exprs.add(parseBinary());
+ } while (currentToken == TOKEN_AND);
+ return factory.and(exprs.toArray(new IExpression[exprs.size()]));
+ }
+
+ protected IExpression parseBinary() {
+ IExpression expr = parseNot();
+ for (;;) {
+ switch (currentToken) {
+ case TOKEN_OR :
+ case TOKEN_AND :
+ case TOKEN_RP :
+ case TOKEN_RB :
+ case TOKEN_RC :
+ case TOKEN_COMMA :
+ case TOKEN_IF :
+ case TOKEN_ELSE :
+ case TOKEN_END :
+ break;
+ case TOKEN_EQUAL :
+ case TOKEN_NOT_EQUAL :
+ case TOKEN_GREATER :
+ case TOKEN_GREATER_EQUAL :
+ case TOKEN_LESS :
+ case TOKEN_LESS_EQUAL :
+ case TOKEN_MATCHES :
+ int realToken = currentToken;
+ nextToken();
+ IExpression rhs;
+ if (realToken == TOKEN_MATCHES && currentToken == TOKEN_LITERAL && tokenValue instanceof String)
+ rhs = factory.constant(new LDAPApproximation((String) tokenValue));
+ else
+ rhs = parseNot();
+ switch (realToken) {
+ case TOKEN_EQUAL :
+ expr = factory.equals(expr, rhs);
+ break;
+ case TOKEN_NOT_EQUAL :
+ expr = factory.not(factory.equals(expr, rhs));
+ break;
+ case TOKEN_GREATER :
+ expr = factory.greater(expr, rhs);
+ break;
+ case TOKEN_GREATER_EQUAL :
+ expr = factory.greaterEqual(expr, rhs);
+ break;
+ case TOKEN_LESS :
+ expr = factory.less(expr, rhs);
+ break;
+ case TOKEN_LESS_EQUAL :
+ expr = factory.lessEqual(expr, rhs);
+ break;
+ default :
+ expr = factory.matches(expr, rhs);
+ }
+ continue;
+ default :
+ throw syntaxError();
+ }
+ break;
+ }
+ return expr;
+ }
+
+ protected IExpression parseNot() {
+ if (currentToken == TOKEN_NOT) {
+ nextToken();
+ IExpression expr = parseNot();
+ return factory.not(expr);
+ }
+ return parseCollectionExpression();
+ }
+
+ protected IExpression parseCollectionExpression() {
+ IExpression expr = parseCollectionLHS();
+ if (expr == null) {
+ expr = parseMember();
+ if (currentToken != TOKEN_DOT)
+ return expr;
+ nextToken();
+ }
+ for (;;) {
+ int funcToken = currentToken;
+ nextToken();
+ assertToken(TOKEN_LP);
+ nextToken();
+ expr = parseCollectionRHS(expr, funcToken);
+ if (currentToken != TOKEN_DOT)
+ break;
+ nextToken();
+ }
+ return expr;
+ }
+
+ protected IExpression parseCollectionLHS() {
+ IExpression expr = null;
+ switch (currentToken) {
+ case TOKEN_EXISTS :
+ case TOKEN_ALL :
+ expr = getVariableOrRootMember(rootVariable);
+ break;
+ }
+ return expr;
+ }
+
+ protected IExpression parseCollectionRHS(IExpression expr, int funcToken) {
+ switch (funcToken) {
+ case TOKEN_EXISTS :
+ expr = factory.exists(expr, parseLambdaDefinition());
+ break;
+ case TOKEN_ALL :
+ expr = factory.all(expr, parseLambdaDefinition());
+ break;
+ default :
+ throw syntaxError();
+ }
+ return expr;
+ }
+
+ protected IExpression parseLambdaDefinition() {
+ assertToken(TOKEN_IDENTIFIER);
+ IExpression each = factory.variable((String) tokenValue);
+ push(each);
+ try {
+ nextToken();
+ assertToken(TOKEN_PIPE);
+ nextToken();
+ IExpression body = parseCondition();
+ assertToken(TOKEN_RP);
+ nextToken();
+ return factory.lambda(each, body);
+ } finally {
+ pop();
+ }
+ }
+
+ protected IExpression parseMember() {
+ IExpression expr = parseUnary();
+ String name;
+ while (currentToken == TOKEN_DOT || currentToken == TOKEN_LB) {
+ int savePos = tokenPos;
+ int saveToken = currentToken;
+ Object saveTokenValue = tokenValue;
+ nextToken();
+ if (saveToken == TOKEN_DOT) {
+ switch (currentToken) {
+ case TOKEN_IDENTIFIER :
+ name = (String) tokenValue;
+ nextToken();
+ expr = factory.member(expr, name);
+ break;
+
+ default :
+ tokenPos = savePos;
+ currentToken = saveToken;
+ tokenValue = saveTokenValue;
+ return expr;
+ }
+ } else {
+ IExpression atExpr = parseMember();
+ assertToken(TOKEN_RB);
+ nextToken();
+ expr = factory.at(expr, atExpr);
+ }
+ }
+ return expr;
+ }
+
+ protected IExpression parseUnary() {
+ IExpression expr;
+ switch (currentToken) {
+ case TOKEN_LP :
+ nextToken();
+ expr = parseCondition();
+ assertToken(TOKEN_RP);
+ nextToken();
+ break;
+ case TOKEN_LITERAL :
+ expr = factory.constant(tokenValue);
+ nextToken();
+ break;
+ case TOKEN_IDENTIFIER :
+ expr = getVariableOrRootMember((String) tokenValue);
+ nextToken();
+ break;
+ case TOKEN_NULL :
+ expr = factory.constant(null);
+ nextToken();
+ break;
+ case TOKEN_TRUE :
+ expr = factory.constant(Boolean.TRUE);
+ nextToken();
+ break;
+ case TOKEN_FALSE :
+ expr = factory.constant(Boolean.FALSE);
+ nextToken();
+ break;
+ case TOKEN_DOLLAR :
+ expr = parseParameter();
+ break;
+ default :
+ throw syntaxError();
+ }
+ return expr;
+ }
+
+ private IExpression parseParameter() {
+ if (currentToken == TOKEN_DOLLAR) {
+ nextToken();
+ if (currentToken == TOKEN_LITERAL && tokenValue instanceof Integer) {
+ IExpression param = factory.indexedParameter(((Integer) tokenValue).intValue());
+ nextToken();
+ return param;
+ }
+ }
+ throw syntaxError();
+ }
+
+ protected IExpression[] parseArray() {
+ IExpression expr = parseCondition();
+ if (currentToken != TOKEN_COMMA)
+ return new IExpression[] {expr};
+
+ ArrayList<IExpression> operands = new ArrayList<IExpression>();
+ operands.add(expr);
+ do {
+ nextToken();
+ if (currentToken == TOKEN_LC)
+ // We don't allow lambdas in the array
+ break;
+ operands.add(parseCondition());
+ } while (currentToken == TOKEN_COMMA);
+ return operands.toArray(new IExpression[operands.size()]);
+ }
+
+ protected void assertToken(int token) {
+ if (currentToken != token)
+ throw syntaxError();
+ }
+
+ protected IExpression getVariableOrRootMember(String id) {
+ int idx = size();
+ while (--idx >= 0) {
+ IExpression v = get(idx);
+ if (id.equals(v.toString()))
+ return v;
+ }
+
+ if (rootVariable == null || rootVariable.equals(id))
+ throw syntaxError("No such variable: " + id); //$NON-NLS-1$
+
+ return factory.member(getVariableOrRootMember(rootVariable), id);
+ }
+
+ protected void nextToken() {
+ tokenValue = null;
+ int top = expression.length();
+ char c = 0;
+ while (tokenPos < top) {
+ c = expression.charAt(tokenPos);
+ if (!Character.isWhitespace(c))
+ break;
+ ++tokenPos;
+ }
+ if (tokenPos >= top) {
+ lastTokenPos = top;
+ currentToken = TOKEN_END;
+ return;
+ }
+
+ lastTokenPos = tokenPos;
+ switch (c) {
+ case '|' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '|') {
+ tokenValue = OPERATOR_OR;
+ currentToken = TOKEN_OR;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_PIPE;
+ ++tokenPos;
+ }
+ break;
+
+ case '&' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '&') {
+ tokenValue = OPERATOR_AND;
+ currentToken = TOKEN_AND;
+ tokenPos += 2;
+ } else
+ currentToken = TOKEN_ERROR;
+ break;
+
+ case '=' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_EQUALS;
+ currentToken = TOKEN_EQUAL;
+ tokenPos += 2;
+ } else
+ currentToken = TOKEN_ERROR;
+ break;
+
+ case '!' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_NOT_EQUALS;
+ currentToken = TOKEN_NOT_EQUAL;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_NOT;
+ ++tokenPos;
+ }
+ break;
+
+ case '~' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_MATCHES;
+ currentToken = TOKEN_MATCHES;
+ tokenPos += 2;
+ } else
+ currentToken = TOKEN_ERROR;
+ break;
+
+ case '>' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_GT_EQUAL;
+ currentToken = TOKEN_GREATER_EQUAL;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_GREATER;
+ ++tokenPos;
+ }
+ break;
+
+ case '<' :
+ if (tokenPos + 1 < top && expression.charAt(tokenPos + 1) == '=') {
+ tokenValue = OPERATOR_LT_EQUAL;
+ currentToken = TOKEN_LESS_EQUAL;
+ tokenPos += 2;
+ } else {
+ currentToken = TOKEN_LESS;
+ ++tokenPos;
+ }
+ break;
+
+ case '?' :
+ currentToken = TOKEN_IF;
+ ++tokenPos;
+ break;
+
+ case ':' :
+ currentToken = TOKEN_ELSE;
+ ++tokenPos;
+ break;
+
+ case '.' :
+ currentToken = TOKEN_DOT;
+ ++tokenPos;
+ break;
+
+ case '$' :
+ currentToken = TOKEN_DOLLAR;
+ ++tokenPos;
+ break;
+
+ case '{' :
+ currentToken = TOKEN_LC;
+ ++tokenPos;
+ break;
+
+ case '}' :
+ currentToken = TOKEN_RC;
+ ++tokenPos;
+ break;
+
+ case '(' :
+ currentToken = TOKEN_LP;
+ ++tokenPos;
+ break;
+
+ case ')' :
+ currentToken = TOKEN_RP;
+ ++tokenPos;
+ break;
+
+ case '[' :
+ currentToken = TOKEN_LB;
+ ++tokenPos;
+ break;
+
+ case ']' :
+ currentToken = TOKEN_RB;
+ ++tokenPos;
+ break;
+
+ case ',' :
+ currentToken = TOKEN_COMMA;
+ ++tokenPos;
+ break;
+
+ case '"' :
+ case '\'' : {
+ int start = ++tokenPos;
+ while (tokenPos < top && expression.charAt(tokenPos) != c)
+ ++tokenPos;
+ if (tokenPos == top) {
+ tokenPos = start - 1;
+ currentToken = TOKEN_ERROR;
+ } else {
+ tokenValue = expression.substring(start, tokenPos++);
+ currentToken = TOKEN_LITERAL;
+ }
+ break;
+ }
+
+ case '/' : {
+ int start = ++tokenPos;
+ StringBuffer buf = new StringBuffer();
+ while (tokenPos < top) {
+ c = expression.charAt(tokenPos);
+ if (c == '\\' && tokenPos + 1 < top) {
+ c = expression.charAt(++tokenPos);
+ if (c != '/')
+ buf.append('\\');
+ } else if (c == '/')
+ break;
+ buf.append(c);
+ ++tokenPos;
+ }
+ if (tokenPos == top) {
+ tokenPos = start - 1;
+ currentToken = TOKEN_ERROR;
+ } else {
+ tokenValue = SimplePattern.compile(expression.substring(start, tokenPos++));
+ currentToken = TOKEN_LITERAL;
+ }
+ break;
+ }
+
+ default :
+ if (Character.isDigit(c)) {
+ int start = tokenPos++;
+ while (tokenPos < top && Character.isDigit(expression.charAt(tokenPos)))
+ ++tokenPos;
+ tokenValue = Integer.valueOf(expression.substring(start, tokenPos));
+ currentToken = TOKEN_LITERAL;
+ break;
+ }
+ if (Character.isJavaIdentifierStart(c)) {
+ int start = tokenPos++;
+ while (tokenPos < top && Character.isJavaIdentifierPart(expression.charAt(tokenPos)))
+ ++tokenPos;
+ String word = expression.substring(start, tokenPos);
+ Integer token = keywordToTokenMap().get(word);
+ if (token == null)
+ currentToken = TOKEN_IDENTIFIER;
+ else
+ currentToken = token.intValue();
+ tokenValue = word;
+ break;
+ }
+ throw syntaxError();
+ }
+ }
+
+ protected void popVariable() {
+ if (isEmpty())
+ throw syntaxError();
+ pop();
+ }
+
+ protected ExpressionParseException syntaxError() {
+ Object tv = tokenValue;
+ if (tv == null) {
+ if (lastTokenPos >= expression.length())
+ return syntaxError("Unexpeced end of expression"); //$NON-NLS-1$
+ tv = expression.substring(lastTokenPos, lastTokenPos + 1);
+ }
+ return syntaxError("Unexpected token \"" + tv + '"'); //$NON-NLS-1$
+ }
+
+ protected ExpressionParseException syntaxError(String message) {
+ return new ExpressionParseException(expression, message, tokenPos);
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/LDAPFilterParser.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/LDAPFilterParser.java
new file mode 100644
index 000000000..b5fd6714c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/expression/parser/LDAPFilterParser.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.expression.parser;
+
+import java.util.ArrayList;
+import org.eclipse.equinox.internal.p2.metadata.Messages;
+import org.eclipse.equinox.internal.p2.metadata.expression.IExpressionConstants;
+import org.eclipse.equinox.internal.p2.metadata.expression.LDAPApproximation;
+import org.eclipse.equinox.p2.metadata.expression.*;
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Parser class for OSGi filter strings. This class parses the complete filter string and builds a tree of Filter
+ * objects rooted at the parent.
+ */
+public class LDAPFilterParser {
+ private final IExpressionFactory factory;
+
+ private final IExpression self;
+
+ private final StringBuffer sb = new StringBuffer();
+
+ private String filterString;
+
+ private int position;
+
+ public LDAPFilterParser(IExpressionFactory factory) {
+ this.factory = factory;
+ self = factory.variable(IExpressionConstants.VARIABLE_THIS);
+ position = 0;
+ }
+
+ public synchronized IFilterExpression parse(String filter) {
+ filterString = filter;
+ position = 0;
+ try {
+ IExpression expr = parseFilter();
+ if (position != filterString.length())
+ throw syntaxException(Messages.filter_trailing_characters);
+ return factory.filterExpression(expr);
+ } catch (StringIndexOutOfBoundsException e) {
+ throw syntaxException(Messages.filter_premature_end);
+ }
+ }
+
+ private IExpression parseAnd() {
+ skipWhiteSpace();
+ char c = filterString.charAt(position);
+ if (c != '(')
+ throw syntaxException(Messages.filter_missing_leftparen);
+
+ ArrayList<IExpression> operands = new ArrayList<IExpression>();
+ while (c == '(') {
+ IExpression child = parseFilter();
+ if (!operands.contains(child))
+ operands.add(child);
+ c = filterString.charAt(position);
+ }
+ // int sz = operands.size();
+ // return sz == 1 ? operands.get(0) : factory.and(operands.toArray(new IExpression[sz]));
+ return factory.normalize(operands, IExpression.TYPE_AND);
+ }
+
+ private IExpression parseAttr() {
+ skipWhiteSpace();
+
+ int begin = position;
+ int end = position;
+
+ char c = filterString.charAt(begin);
+ while (!(c == '~' || c == '<' || c == '>' || c == '=' || c == '(' || c == ')')) {
+ position++;
+ if (!Character.isWhitespace(c))
+ end = position;
+ c = filterString.charAt(position);
+ }
+ if (end == begin)
+ throw syntaxException(Messages.filter_missing_attr);
+ return factory.member(self, filterString.substring(begin, end));
+ }
+
+ private IExpression parseFilter() {
+ IExpression filter;
+ skipWhiteSpace();
+
+ if (filterString.charAt(position) != '(')
+ throw syntaxException(Messages.filter_missing_leftparen);
+
+ position++;
+ filter = parseFiltercomp();
+
+ skipWhiteSpace();
+
+ if (filterString.charAt(position) != ')')
+ throw syntaxException(Messages.filter_missing_rightparen);
+
+ position++;
+ skipWhiteSpace();
+
+ return filter;
+ }
+
+ private IExpression parseFiltercomp() {
+ skipWhiteSpace();
+
+ char c = filterString.charAt(position);
+
+ switch (c) {
+ case '&' : {
+ position++;
+ return parseAnd();
+ }
+ case '|' : {
+ position++;
+ return parseOr();
+ }
+ case '!' : {
+ position++;
+ return parseNot();
+ }
+ }
+ return parseItem();
+ }
+
+ private IExpression parseItem() {
+ IExpression attr = parseAttr();
+
+ skipWhiteSpace();
+ String value;
+
+ boolean[] hasWild = {false};
+ char c = filterString.charAt(position);
+ switch (c) {
+ case '~' :
+ case '>' :
+ case '<' :
+ if (filterString.charAt(position + 1) != '=')
+ throw syntaxException(Messages.filter_invalid_operator);
+ position += 2;
+ int savePos = position;
+ value = parseValue(hasWild);
+ if (hasWild[0]) {
+ // Unescaped wildcard found. This is not legal for the given operator
+ position = savePos;
+ throw syntaxException(Messages.filter_invalid_value);
+ }
+ switch (c) {
+ case '>' :
+ return factory.greaterEqual(attr, factory.constant(value));
+ case '<' :
+ return factory.lessEqual(attr, factory.constant(value));
+ }
+ return factory.matches(attr, factory.constant(new LDAPApproximation(value)));
+ case '=' :
+ position++;
+ value = parseValue(hasWild);
+ return hasWild[0] ? factory.matches(attr, factory.constant(SimplePattern.compile(value))) : factory.equals(attr, factory.constant(value));
+ }
+ throw syntaxException(Messages.filter_invalid_operator);
+ }
+
+ private IExpression parseNot() {
+ skipWhiteSpace();
+
+ if (filterString.charAt(position) != '(')
+ throw syntaxException(Messages.filter_missing_leftparen);
+ return factory.not(parseFilter());
+ }
+
+ private IExpression parseOr() {
+ skipWhiteSpace();
+ char c = filterString.charAt(position);
+ if (c != '(')
+ throw syntaxException(Messages.filter_missing_leftparen);
+
+ ArrayList<IExpression> operands = new ArrayList<IExpression>();
+ while (c == '(') {
+ IExpression child = parseFilter();
+ operands.add(child);
+ c = filterString.charAt(position);
+ }
+ // int sz = operands.size();
+ // return sz == 1 ? operands.get(0) : factory.or(operands.toArray(new IExpression[sz]));
+ return factory.normalize(operands, IExpression.TYPE_OR);
+ }
+
+ private static int hexValue(char c) {
+ int v;
+ if (c <= '9')
+ v = c - '0';
+ else if (c <= 'F')
+ v = (c - 'A') + 10;
+ else
+ v = (c - 'a') + 10;
+ return v;
+ }
+
+ private String parseValue(boolean[] hasWildBin) {
+ sb.setLength(0);
+ int savePos = position;
+ boolean hasEscapedWild = false;
+ parseloop: while (true) {
+ char c = filterString.charAt(position);
+ switch (c) {
+ case '*' :
+ if (hasEscapedWild && !hasWildBin[0]) {
+ // We must redo the parse.
+ position = savePos;
+ hasWildBin[0] = true;
+ return parseValue(hasWildBin);
+ }
+ hasWildBin[0] = true;
+ sb.append(c);
+ position++;
+ break;
+
+ case ')' :
+ break parseloop;
+
+ case '(' :
+ throw syntaxException(Messages.filter_invalid_value);
+
+ case '\\' :
+ c = filterString.charAt(++position);
+ if (c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f' && position + 1 < filterString.length()) {
+ char nc = filterString.charAt(position + 1);
+ if (nc >= '0' && nc <= '9' || nc >= 'A' && nc <= 'F' || nc >= 'a' && nc <= 'f') {
+ // Assume proper \xx escape where xx are hex digits
+ ++position;
+ c = (char) (((hexValue(c) << 4) & 0xf0) | (hexValue(nc) & 0x0f));
+ if (c == '*' && hasWildBin != null) {
+ hasEscapedWild = true;
+ if (hasWildBin[0])
+ sb.append('\\');
+ }
+ }
+ }
+ /* fall through into default */
+
+ default :
+ sb.append(c);
+ position++;
+ break;
+ }
+ }
+ if (sb.length() == 0)
+ throw syntaxException(Messages.filter_missing_value);
+ return sb.toString();
+ }
+
+ private void skipWhiteSpace() {
+ for (int top = filterString.length(); position < top; ++position)
+ if (!Character.isWhitespace(filterString.charAt(position)))
+ break;
+ }
+
+ protected ExpressionParseException syntaxException(String message) {
+ return new ExpressionParseException(NLS.bind(message, filterString, Integer.toString(position)));
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/messages.properties b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/messages.properties
index 2d675ff07..a70676f45 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/messages.properties
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/messages.properties
@@ -8,8 +8,6 @@
# Contributors:
# Cloudsmith Inc. - initial API and implementation
###############################################################################
-_0_is_not_a_positive_integer_in_osgi_1=The {0} value is not a positive in OSGi version: "{1}"
-_0_is_not_a_string_in_osgi_1=The {0} value is not a string in OSGi version: "{1}"
_0_is_not_a_valid_qualifier_in_osgi_1=The {0} value is invalid in OSGi version: "{1}"
array_can_not_be_empty=Array format can not be empty
array_can_not_have_character_group=Array format can not have a character group
@@ -27,6 +25,14 @@ EOS_after_escape=End of string was encountere after the escape character
expected_orignal_after_colon_0=An original version was expected after colon: {0}
expected_orignal_after_slash_0=A format or colon was expected after slash: {0}
expected_slash_after_raw_vector_0=A slash was expected after a raw version: {0}
+filter_missing_leftparen=Filter "{0}" Missing ''('' at position {1}
+filter_missing_rightparen=Filter "{0}" Missing '')'' at position {1}
+filter_trailing_characters=Filter "{0}" Extraneous trailing characters at position {1}
+filter_invalid_operator=Filter "{0}" Invalid operator at position {1}
+filter_missing_attr=Filter "{0}" Missing attr at position {1}
+filter_missing_value=Filter "{0}" is missing value at position {1}
+filter_invalid_value=Filter "{0}" has invalid value at position {1}
+filter_premature_end=Filter "{0}" ends before it is complete
format_0_unable_to_parse_1=Format "{0}" was unable to parse {1}
format_0_unable_to_parse_empty_version=Format "{0}" was unable to parse an empty version
format_is_empty=Format is empty
@@ -34,16 +40,15 @@ format_must_be_delimited_by_colon_0=Format must be delimited by version range: {
group_can_not_be_empty=A group can not be empty
ignore_defined_more_then_once=More then one definition of ignore
illegal_character_encountered_ascii_0=An illegal character was encountered. Code = {0}
-illegal_number_of_entries_0_in_osgi_1=Illegal number of entries {0} in OSGi version: "{1}"
missing_comma_in_range_0=Missing comma in range "{0}"
negative_character_range=The character range is negative
neither_raw_vector_nor_format_specified_0=Neither raw version nor format was specified: {0}
number_can_not_have_pad_value=A number cannot have a pad value
only_format_specified_0=Only a format was specified: {0}
+only_max_and_empty_string_defaults_can_have_translations=Only max string and empty string defaults can have translations
original_must_start_with_colon_0=Original version must start with colon: {0}
original_stated_but_missing_0=Expected original version after colon: {0}
pad_defined_more_then_once=Pad was defined more then once
-pad_not_allowed_in_osgi_0=Pad is not allowed in an OSGi version: "{0}"
performing_subquery=Performing subquery
premature_end_of_format=Premature end of format
premature_end_of_format_expected_0=Premature end of format, "{0}" expected
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IUPropertyQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/IUPropertyQuery.java
index 9b43371dc..95f3f3375 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IUPropertyQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/IUPropertyQuery.java
@@ -8,16 +8,17 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.internal.p2.metadata.query;
-import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.query.MatchQuery;
/**
* A query that searches for {@link IInstallableUnit} instances that have
* a property whose value matches the provided value. If no property name is
* specified, then all {@link IInstallableUnit} instances are accepted.
*/
-public class IUPropertyQuery extends MatchQuery {
+public class IUPropertyQuery extends MatchQuery<IInstallableUnit> {
private String propertyName;
private String propertyValue;
@@ -32,10 +33,7 @@ public class IUPropertyQuery extends MatchQuery {
/* (non-Javadoc)
* @see org.eclipse.equinox.p2.query2.Query#isMatch(java.lang.Object)
*/
- public boolean isMatch(Object object) {
- if (!(object instanceof IInstallableUnit))
- return false;
- IInstallableUnit candidate = (IInstallableUnit) object;
+ public boolean isMatch(IInstallableUnit candidate) {
if (propertyName == null)
return true;
String value = getProperty(candidate, propertyName);
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/LatestIUVersionQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/LatestIUVersionQuery.java
new file mode 100644
index 000000000..fe79d9404
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/LatestIUVersionQuery.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+* Copyright (c) 2009 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
+*
+* Contributors:
+* EclipseSource - initial API and implementation
+******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.query;
+
+import java.util.*;
+import org.eclipse.equinox.p2.metadata.IVersionedId;
+import org.eclipse.equinox.p2.query.*;
+
+/**
+ * This query returns the latest version for each unique VersionedID.
+ * All other elements are discarded.
+ */
+public class LatestIUVersionQuery<T extends IVersionedId> extends ContextQuery<T> {
+
+ /**
+ * Performs the LatestIUVersionQuery
+ */
+ public IQueryResult<T> perform(Iterator<T> iterator) {
+ HashMap<String, T> greatestIUVersion = new HashMap<String, T>();
+ while (iterator.hasNext()) {
+ T versionedID = iterator.next();
+ if (greatestIUVersion.containsKey(versionedID.getId())) {
+ T currentIU = greatestIUVersion.get(versionedID.getId());
+ if (currentIU.getVersion().compareTo(versionedID.getVersion()) < 0)
+ greatestIUVersion.put(versionedID.getId(), versionedID);
+ } else
+ greatestIUVersion.put(versionedID.getId(), versionedID);
+ }
+
+ Collection<T> values = greatestIUVersion.values();
+ Iterator<T> valuesIterator = values.iterator();
+ boolean continueGather = true;
+
+ Collector<T> result = new Collector<T>();
+ while (valuesIterator.hasNext() && continueGather) {
+ T nextIU = valuesIterator.next();
+ continueGather = result.accept(nextIU);
+ }
+ return result;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/ObjectMatchQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/ObjectMatchQuery.java
new file mode 100644
index 000000000..f48e50aac
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/ObjectMatchQuery.java
@@ -0,0 +1,22 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.query;
+
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * Special implementation for use without generic support
+ */
+public abstract class ObjectMatchQuery extends MatchQuery<Object> {
+ public boolean isMatch(Object candidate) {
+ return true;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/UpdateQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/UpdateQuery.java
new file mode 100644
index 000000000..3ca68f9a0
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/query/UpdateQuery.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.internal.p2.metadata.query;
+
+import org.eclipse.equinox.p2.metadata.IUpdateDescriptor;
+
+import org.eclipse.equinox.p2.metadata.IInstallableUnitPatch;
+
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IRequirement;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * A query that finds all IUs that are considered an "Update" of the
+ * specified IU.
+ */
+public class UpdateQuery extends MatchQuery<IInstallableUnit> {
+ private IInstallableUnit updateFrom;
+
+ public UpdateQuery(IInstallableUnit updateFrom) {
+ this.updateFrom = updateFrom;
+ }
+
+ public boolean isMatch(IInstallableUnit candidate) {
+ if (candidate instanceof IInstallableUnitPatch && !(updateFrom instanceof IInstallableUnitPatch)) {
+ IInstallableUnitPatch potentialPatch = (IInstallableUnitPatch) candidate;
+ IRequirement lifeCycle = potentialPatch.getLifeCycle();
+ if (lifeCycle == null)
+ return false;
+ return updateFrom.satisfies(lifeCycle);
+ }
+ IUpdateDescriptor descriptor = candidate.getUpdateDescriptor();
+ if (descriptor != null && descriptor.isUpdateOf(updateFrom)) {
+ if (!updateFrom.getId().equals(candidate.getId()))
+ return true;
+ return updateFrom.getVersion().compareTo(candidate.getVersion()) < 0;
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/QueryHelpers.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/query/QueryHelpers.java
index 0d0a54d82..794ceff2b 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/QueryHelpers.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/query/QueryHelpers.java
@@ -7,20 +7,25 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.internal.p2.query;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import org.eclipse.equinox.p2.query.IQuery;
/**
* Static helper methods for the Query API.
+ *
+ * @noextend This class is not intended to be subclassed by clients.
*/
public class QueryHelpers {
/**
* Gets the ID for a Query.
+ *
+ * @noreference This method is not intended to be referenced by clients.
*/
- public static String getId(Query query) {
+ public static String getId(IQuery<?> query) {
return query.getClass().getName();
}
@@ -28,9 +33,11 @@ public class QueryHelpers {
* Gets a particular property of a query.
* @param query The query to retrieve the property from
* @param property The property to retrieve
+ *
+ * @noreference This method is not intended to be referenced by clients.
*/
- public static Object getProperty(Query query, String property) {
- Class clazz = query.getClass();
+ public static Object getProperty(IQuery<?> query, String property) {
+ Class<?> clazz = query.getClass();
Object result = null;
try {
Method method = clazz.getMethod("get" + property, new Class[0]); //$NON-NLS-1$
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnit.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnit.java
deleted file mode 100644
index b06c7337a..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnit.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007, 2008 IBM Corporation 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:
- * IBM Corporation - initial API and implementation
- * Genuitec, LLC - added license support
- * EclipseSource - ongoing development
- ******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
-import java.util.Map;
-
-/**
- * @noimplement This interface is not intended to be implemented by clients.
- * @noextend This interface is not intended to be extended by clients.
- */
-public interface IInstallableUnit extends IVersionedId, Comparable {
-
- /**
- * A capability namespace representing a particular profile flavor.
- */
- public static final String NAMESPACE_FLAVOR = "org.eclipse.equinox.p2.flavor"; //$NON-NLS-1$
-
- /**
- * A capability namespace representing a particular InstallableUnit by id.
- * Each InstallableUnit automatically provides a capability in this namespace representing
- * itself, and other InstallableUnits can require such a capability to state that they
- * require a particular InstallableUnit to be present.
- *
- * @see IInstallableUnit#getId()
- */
- public static final String NAMESPACE_IU_ID = "org.eclipse.equinox.p2.iu"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.partial.iu"</code>) for a
- * boolean property indicating the IU is generated from incomplete information and
- * should be replaced by the complete IU if available.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_PARTIAL_IU = "org.eclipse.equinox.p2.partial.iu"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.type.profile"</code>) for a
- * boolean property indicating that an installable unit is a profile.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_TYPE_PROFILE = "org.eclipse.equinox.p2.type.profile"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.type.category"</code>) for a
- * boolean property indicating that an installable unit is a category.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_TYPE_CATEGORY = "org.eclipse.equinox.p2.type.category"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.type.fragment"</code>) for a
- * boolean property indicating that an installable unit is a fragment.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_TYPE_FRAGMENT = "org.eclipse.equinox.p2.type.fragment"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.type.group"</code>) for a
- * boolean property indicating that an installable unit is a group.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_TYPE_GROUP = "org.eclipse.equinox.p2.type.group"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.type.patch"</code>) for a
- * boolean property indicating that an installable unit is a group.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_TYPE_PATCH = "org.eclipse.equinox.p2.type.patch"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.type.lock"</code>) for an
- * integer property indicating how an installable unit is locked in its profile.
- * The integer is a bit-mask indicating the different locks defined on the installable
- * unit. The property should be obtained from a profile using
- * IProfile#getInstallableUnitProperty(IInstallableUnit, String).
- *
- * @see #LOCK_UNINSTALL
- * @see #LOCK_UPDATE
- * @see #LOCK_NONE
- */
- public static final String PROP_PROFILE_LOCKED_IU = "org.eclipse.equinox.p2.type.lock"; //$NON-NLS-1$
-
- //TODO Move to UI
- public static final String PROP_PROFILE_ROOT_IU = "org.eclipse.equinox.p2.type.root"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.contact"</code>) for a
- * String property containing a contact address where problems can be reported,
- * such as an email address.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_CONTACT = "org.eclipse.equinox.p2.contact"; //$NON-NLS-1$
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.description"</code>) for a
- * String property containing a human-readable description of the installable unit.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_DESCRIPTION = "org.eclipse.equinox.p2.description"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.description.url"</code>) for a
- * String property containing a URL to the description of the installable unit.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_DESCRIPTION_URL = "org.eclipse.equinox.p2.description.url"; //$NON-NLS-1$
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.doc.url"</code>) for a
- * String property containing a URL for documentation about the installable unit.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_DOC_URL = "org.eclipse.equinox.p2.doc.url"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.bundle.localization"</code>) for a String
- * property containing the bundle localization property file name
- */
- public static final String PROP_BUNDLE_LOCALIZATION = "org.eclipse.equinox.p2.bundle.localization"; //$NON-NLS-1$
-
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.name"</code>) for a
- * String property containing a human-readable name for the installable unit.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_NAME = "org.eclipse.equinox.p2.name"; //$NON-NLS-1$
- /**
- * A property key (value <code>"org.eclipse.equinox.p2.provider"</code>) for a
- * String property containing information about the vendor or provider of the
- * installable unit.
- *
- * @see #getProperty(String)
- */
- public static final String PROP_PROVIDER = "org.eclipse.equinox.p2.provider"; //$NON-NLS-1$
-
- /**
- * Constant used to indicate that an installable unit is not locked in anyway.
- * @see #PROP_PROFILE_LOCKED_IU
- */
- public static int LOCK_NONE = 0;
- /**
- * Constant used to indicate that an installable unit is locked so that it may
- * not be uninstalled.
- * @see #PROP_PROFILE_LOCKED_IU
- */
- public static int LOCK_UNINSTALL = 1 << 0;
- /**
- * Constant used to indicate that an installable unit is locked so that it may
- * not be updated. updates.
- * @see #PROP_PROFILE_LOCKED_IU
- */
- public static int LOCK_UPDATE = 1 << 1;
-
- public IArtifactKey[] getArtifacts();
-
- /**
- * Returns the filter on this installable unit. The filter is matched against
- * the selection context of the profile the unit is installed into. An IU will not
- * be installed if it has a filter condition that is not satisfied by the context.
- *
- * See Profile#getSelectionContext.
- */
- public String getFilter();
-
- /**
- * Returns the fragments that have been bound to this installable unit, or
- * <code>null</code> if this unit is not resolved.
- *
- * @see #isResolved()
- * @return The fragments bound to this installable unit, or <code>null</code>
- */
- public IInstallableUnitFragment[] getFragments();
-
- /**
- * Get an <i>unmodifiable copy</i> of the properties
- * associated with the installable unit.
- *
- * @return an <i>unmodifiable copy</i> of the IU properties.
- */
- public Map getProperties();
-
- public String getProperty(String key);
-
- public IProvidedCapability[] getProvidedCapabilities();
-
- public IRequiredCapability[] getRequiredCapabilities();
-
- public IRequiredCapability[] getMetaRequiredCapabilities();
-
- public ITouchpointData[] getTouchpointData();
-
- public ITouchpointType getTouchpointType();
-
- public boolean isFragment();
-
- /**
- * Returns whether this installable unit has been resolved. A resolved
- * installable unit represents the union of an installable unit and some
- * fragments.
- *
- * @see #getFragments()
- * @see #unresolved()
- * @return <code>true</code> if this installable unit is resolved, and
- * <code>false</code> otherwise.
- */
- public boolean isResolved();
-
- public boolean isSingleton();
-
- /**
- * Returns whether this unit has a provided capability that satisfies the given
- * required capability.
- * @return <code>true</code> if this unit satisfies the given required
- * capability, and <code>false</code> otherwise.
- */
- public boolean satisfies(IRequiredCapability candidate);
-
- /**
- * Returns the unresolved equivalent of this installable unit. If this unit is
- * already unresolved, this method returns the receiver. Otherwise, this
- * method returns an installable unit with the same id and version, but without
- * any fragments attached.
- *
- * @see #getFragments()
- * @see #isResolved()
- * @return The unresolved equivalent of this unit
- */
- public IInstallableUnit unresolved();
-
- /**
- * Returns information about what this installable unit is an update of.
- * @return The lineage information about the installable unit
- */
- public IUpdateDescriptor getUpdateDescriptor();
-
- /**
- * Returns the license that applies to this installable unit.
- * @return the license that applies to this installable unit or <code>null</code>
- */
- public ILicense getLicense();
-
- /**
- * Returns the copyright that applies to this installable unit.
- * @return the copyright that applies to this installable unit or <code>null</code>
- */
- public ICopyright getCopyright();
-
- /**
- * Returns whether this InstallableUnit is equal to the given object.
- *
- * This method returns <i>true</i> if:
- * <ul>
- * <li> Both this object and the given object are of type IInstallableUnit
- * <li> The result of <b>getId()</b> on both objects are equal
- * <li> The result of <b>getVersion()</b> on both objects are equal
- * </ul>
- */
- public boolean equals(Object obj);
-} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequiredCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequiredCapability.java
deleted file mode 100644
index 9e0b652e6..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequiredCapability.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*******************************************************************************
-* Copyright (c) 2008, 2009 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
-*
-* Contributors:
-* EclipseSource - initial API and implementation
-******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
-/**
- * A required capability represents some external constraint on an {@link IInstallableUnit}.
- * Each capability represents something an {@link IInstallableUnit} needs that
- * it expects to be provided by another {@link IInstallableUnit}. Capabilities are
- * entirely generic, and are intended to be capable of representing anything that
- * an {@link IInstallableUnit} may need either at install time, or at runtime.
- * <p>
- * Capabilities are segmented into namespaces. Anyone can introduce new
- * capability namespaces. Some well-known namespaces are introduced directly
- * by the provisioning framework.
- *
- * @see IInstallableUnit#NAMESPACE_IU_ID
- *
- * @noimplement This interface is not intended to be implemented by clients.
- * @noextend This interface is not intended to be extended by clients.
- */
-public interface IRequiredCapability {
-
- public String getFilter();
-
- public String getName();
-
- public String getNamespace();
-
- /**
- * Returns the range of versions that satisfy this required capability. Returns
- * an empty version range ({@link VersionRange#emptyRange} if any version
- * will satisfy the capability.
- * @return the range of versions that satisfy this required capability.
- */
- public VersionRange getRange();
-
- /**
- * Returns the properties to use for evaluating required capability filters
- * downstream from this capability. For example, if the selector "doc"
- * is provided, then a downstream InstallableUnit with a required capability
- * filtered with "doc=true" will be included.
- */
- public String[] getSelectors();
-
- public boolean isMultiple();
-
- public boolean isOptional();
-
- /**
- * TODO This object shouldn't be mutable since it makes equality unstable, and
- * introduces lifecycle issues (how are the changes persisted, etc)
- */
- public void setFilter(String filter);
-
- /**
- * TODO This object shouldn't be mutable since it makes equality unstable, and
- * introduces lifecycle issues (how are the changes persisted, etc)
- */
- public void setSelectors(String[] selectors);
-
- public boolean isGreedy();
-
- /**
- * Returns whether this required capability is equal to the given object.
- *
- * This method returns <i>true</i> if:
- * <ul>
- * <li> Both this object and the given object are of type IRequiredCapability
- * <li> The result of <b>getFilter()</b> on both objects are equal
- * <li> The result of <b>isMultiple()</b> on both objects are equal
- * <li> The result of <b>getName()</b> on both objects are equal
- * <li> The result of <b>geNamespace()</b> on both objects are equal
- * <li> The result of <b>isOptional()</b> on both objects are equal
- * <li> The result of <b>getRange()</b> on both objects are equal
- * </ul>
- */
- public boolean equals(Object other);
-
- public boolean isNegation();
-
- public boolean satisfiedBy(IProvidedCapability cap);
-
-} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/MetadataFactory.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/MetadataFactory.java
index b1021d5b4..4e023f487 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/MetadataFactory.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/MetadataFactory.java
@@ -16,7 +16,10 @@ import java.net.URI;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
import org.eclipse.equinox.internal.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.*;
+import org.osgi.framework.Filter;
/**
* A factory class for instantiating various p2 metadata objects.
@@ -29,34 +32,53 @@ public class MetadataFactory {
* the resulting immutable unit.
*/
public static class InstallableUnitDescription {
+ public static final String PROP_TYPE_GROUP = "org.eclipse.equinox.p2.type.group"; //$NON-NLS-1$
+
protected InstallableUnit unit;
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.type.patch"</code>) for a
+ * boolean property indicating that an installable unit is a group.
+ *
+ */
+ public static final String PROP_TYPE_PATCH = "org.eclipse.equinox.p2.type.patch"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.type.fragment"</code>) for a
+ * boolean property indicating that an installable unit is a fragment.
+ *
+ */
+ public static final String PROP_TYPE_FRAGMENT = "org.eclipse.equinox.p2.type.fragment"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.type.category"</code>) for a
+ * boolean property indicating that an installable unit is a category.
+ *
+ */
+ public static final String PROP_TYPE_CATEGORY = "org.eclipse.equinox.p2.type.category"; //$NON-NLS-1$
+
public InstallableUnitDescription() {
super();
}
- public void addProvidedCapabilities(Collection additional) {
+ public void addProvidedCapabilities(Collection<IProvidedCapability> additional) {
if (additional == null || additional.size() == 0)
return;
- IProvidedCapability[] current = unit().getProvidedCapabilities();
- IProvidedCapability[] result = new IProvidedCapability[additional.size() + current.length];
- System.arraycopy(current, 0, result, 0, current.length);
- int j = current.length;
- for (Iterator i = additional.iterator(); i.hasNext();)
- result[j++] = (IProvidedCapability) i.next();
- unit().setCapabilities(result);
+ Collection<IProvidedCapability> current = unit().getProvidedCapabilities();
+ ArrayList<IProvidedCapability> all = new ArrayList<IProvidedCapability>(additional.size() + current.size());
+ all.addAll(current);
+ all.addAll(additional);
+ unit().setCapabilities(all.toArray(new IProvidedCapability[all.size()]));
}
- public void addRequiredCapabilities(Collection additional) {
+ public void addRequiredCapabilities(Collection<IRequirement> additional) {
if (additional == null || additional.size() == 0)
return;
- IRequiredCapability[] current = unit().getRequiredCapabilities();
- IRequiredCapability[] result = new IRequiredCapability[additional.size() + current.length];
- System.arraycopy(current, 0, result, 0, current.length);
- int j = current.length;
- for (Iterator i = additional.iterator(); i.hasNext();)
- result[j++] = (IRequiredCapability) i.next();
- unit().setRequiredCapabilities(result);
+ List<IRequirement> current = unit().getRequiredCapabilities();
+ ArrayList<IRequirement> all = new ArrayList<IRequirement>(additional.size() + current.size());
+ all.addAll(current);
+ all.addAll(additional);
+ unit().setRequiredCapabilities(all.toArray(new IRequirement[all.size()]));
}
public void addTouchpointData(ITouchpointData data) {
@@ -68,15 +90,15 @@ public class MetadataFactory {
return unit().getId();
}
- public IProvidedCapability[] getProvidedCapabilities() {
+ public Collection<IProvidedCapability> getProvidedCapabilities() {
return unit().getProvidedCapabilities();
}
- public IRequiredCapability[] getRequiredCapabilities() {
+ public List<IRequirement> getRequiredCapabilities() {
return unit().getRequiredCapabilities();
}
- public IRequiredCapability[] getMetaRequiredCapabilities() {
+ public Collection<IRequirement> getMetaRequiredCapabilities() {
return unit().getMetaRequiredCapabilities();
}
@@ -86,7 +108,7 @@ public class MetadataFactory {
*
* @return The current touchpoint data on this description
*/
- public ITouchpointData[] getTouchpointData() {
+ public List<ITouchpointData> getTouchpointData() {
return unit().getTouchpointData();
}
@@ -107,6 +129,10 @@ public class MetadataFactory {
unit().setCopyright(copyright);
}
+ public void setFilter(Filter filter) {
+ unit().setFilter(filter);
+ }
+
public void setFilter(String filter) {
unit().setFilter(filter);
}
@@ -115,19 +141,19 @@ public class MetadataFactory {
unit().setId(id);
}
- public void setLicense(ILicense license) {
- unit().setLicense(license);
+ public void setLicenses(ILicense[] licenses) {
+ unit().setLicenses(licenses);
}
public void setProperty(String key, String value) {
unit().setProperty(key, value);
}
- public void setRequiredCapabilities(IRequiredCapability[] requirements) {
+ public void setRequiredCapabilities(IRequirement[] requirements) {
unit().setRequiredCapabilities(requirements);
}
- public void setMetaRequiredCapabilities(IRequiredCapability[] metaRequirements) {
+ public void setMetaRequiredCapabilities(IRequirement[] metaRequirements) {
unit().setMetaRequiredCapabilities(metaRequirements);
}
@@ -162,17 +188,13 @@ public class MetadataFactory {
}
}
- /**
- * Description of an installable unit patch. The description will automatically have
- * the {@link IInstallableUnit#PROP_TYPE_FRAGMENT} set to <code>true</code>.
- */
public static class InstallableUnitFragmentDescription extends InstallableUnitDescription {
public InstallableUnitFragmentDescription() {
super();
- setProperty(IInstallableUnit.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString());
+ setProperty(InstallableUnitDescription.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString());
}
- public void setHost(IRequiredCapability[] hostRequirements) {
+ public void setHost(IRequirement[] hostRequirements) {
((InstallableUnitFragment) unit()).setHost(hostRequirements);
}
@@ -183,24 +205,20 @@ public class MetadataFactory {
}
}
- /**
- * Description of an installable unit patch. The description will automatically have
- * the {@link IInstallableUnit#PROP_TYPE_PATCH} set to <code>true</code>.
- */
public static class InstallableUnitPatchDescription extends InstallableUnitDescription {
public InstallableUnitPatchDescription() {
super();
- setProperty(IInstallableUnit.PROP_TYPE_PATCH, Boolean.TRUE.toString());
+ setProperty(InstallableUnitDescription.PROP_TYPE_PATCH, Boolean.TRUE.toString());
}
- public void setApplicabilityScope(IRequiredCapability[][] applyTo) {
+ public void setApplicabilityScope(IRequirement[][] applyTo) {
if (applyTo == null)
throw new IllegalArgumentException("A patch scope can not be null"); //$NON-NLS-1$
((InstallableUnitPatch) unit()).setApplicabilityScope(applyTo);
}
- public void setLifeCycle(IRequiredCapability lifeCycle) {
+ public void setLifeCycle(IRequirement lifeCycle) {
((InstallableUnitPatch) unit()).setLifeCycle(lifeCycle);
}
@@ -211,7 +229,7 @@ public class MetadataFactory {
InstallableUnit unit() {
if (unit == null) {
unit = new InstallableUnitPatch();
- ((InstallableUnitPatch) unit()).setApplicabilityScope(new IRequiredCapability[0][0]);
+ ((InstallableUnitPatch) unit()).setApplicabilityScope(new IRequirement[0][0]);
}
return unit;
}
@@ -220,7 +238,7 @@ public class MetadataFactory {
/**
* Singleton touchpoint data for a touchpoint with no instructions.
*/
- private static final ITouchpointData EMPTY_TOUCHPOINT_DATA = new TouchpointData(Collections.EMPTY_MAP);
+ private static final ITouchpointData EMPTY_TOUCHPOINT_DATA = new TouchpointData(CollectionUtils.<String, ITouchpointInstruction> emptyMap());
private static ITouchpointType[] typeCache = new ITouchpointType[5];
@@ -277,7 +295,7 @@ public class MetadataFactory {
}
/**
- * Returns a {@link IRequiredCapability} with the given values.
+ * Returns a {@link IRequirement} with the given values.
*
* @param namespace The capability namespace
* @param name The required capability name
@@ -289,8 +307,12 @@ public class MetadataFactory {
* and <code>false</code> otherwise.
* @param multiple <code>true</code> if this capability can be satisfied by multiple provided capabilities, or it requires exactly one match
*/
- public static IRequiredCapability createRequiredCapability(String namespace, String name, VersionRange range, String filter, boolean optional, boolean multiple) {
- return new RequiredCapability(namespace, name, range, filter, optional, multiple);
+ public static IRequiredCapability createRequiredCapability(String namespace, String name, VersionRange range, Filter filter, boolean optional, boolean multiple) {
+ return new RequiredCapability(namespace, name, range, filter, optional ? 0 : 1, multiple ? Integer.MAX_VALUE : 1, true);
+ }
+
+ public static IRequirement createRequiredCapability(String namespace, String name, VersionRange range, Filter filter, int minCard, int maxCard, boolean greedy) {
+ return new RequiredCapability(namespace, name, range, filter, minCard, maxCard, greedy);
}
public static IRequiredCapability createRequiredCapability(String namespace, String name, VersionRange range, String filter, boolean optional, boolean multiple, boolean greedy) {
@@ -303,8 +325,10 @@ public class MetadataFactory {
* @param newValue The result of the requirement change - the requirement to replace the source requirement with
* @return a requirement change
*/
- public static IRequirementChange createRequirementChange(IRequiredCapability applyOn, IRequiredCapability newValue) {
- return new RequirementChange(applyOn, newValue);
+ public static IRequirementChange createRequirementChange(IRequirement applyOn, IRequirement newValue) {
+ if ((applyOn == null || applyOn instanceof IRequiredCapability) && (newValue == null || newValue instanceof IRequiredCapability))
+ return new RequirementChange((IRequiredCapability) applyOn, (IRequiredCapability) newValue);
+ throw new IllegalArgumentException();
}
/**
@@ -327,7 +351,7 @@ public class MetadataFactory {
* @throws IllegalArgumentException when the <code>body</code> is <code>null</code>
*/
public static ILicense createLicense(URI location, String body) {
- return new License(location, body);
+ return new License(location, body, null);
}
/**
@@ -354,24 +378,66 @@ public class MetadataFactory {
* @param instructions The instructions for the touchpoint data.
* @return The created touchpoint data
*/
- public static ITouchpointData createTouchpointData(Map instructions) {
+ public static ITouchpointData createTouchpointData(Map<String, ? extends Object> instructions) {
Assert.isNotNull(instructions);
//copy the map to protect against subsequent change by caller
if (instructions.isEmpty())
return EMPTY_TOUCHPOINT_DATA;
- Map result = new LinkedHashMap(instructions.size());
- for (Iterator iterator = instructions.entrySet().iterator(); iterator.hasNext();) {
- Entry entry = (Entry) iterator.next();
+ Map<String, ITouchpointInstruction> result = new LinkedHashMap<String, ITouchpointInstruction>(instructions.size());
+
+ for (Entry<String, ? extends Object> entry : instructions.entrySet()) {
Object value = entry.getValue();
+ ITouchpointInstruction instruction;
if (value == null || value instanceof String)
- value = createTouchpointInstruction((String) value, null);
-
- result.put(entry.getKey(), value);
+ instruction = createTouchpointInstruction((String) value, null);
+ else
+ instruction = (ITouchpointInstruction) value;
+ result.put(entry.getKey(), instruction);
}
return new TouchpointData(result);
}
+ /**
+ * Merge the given touchpoint instructions with a pre-existing touchpoint data
+ * @param initial - the initial ITouchpointData
+ * @param incomingInstructions - Map of ITouchpointInstructions to merge into the initial touchpoint data
+ * @return the merged ITouchpointData
+ */
+ public static ITouchpointData mergeTouchpointData(ITouchpointData initial, Map<String, ITouchpointInstruction> incomingInstructions) {
+ if (incomingInstructions == null || incomingInstructions.size() == 0)
+ return initial;
+
+ Map<String, ITouchpointInstruction> resultInstructions = new HashMap<String, ITouchpointInstruction>(initial.getInstructions());
+ for (String key : incomingInstructions.keySet()) {
+ ITouchpointInstruction instruction = incomingInstructions.get(key);
+ ITouchpointInstruction existingInstruction = resultInstructions.get(key);
+
+ if (existingInstruction != null) {
+ String body = existingInstruction.getBody();
+ if (body == null || body.length() == 0)
+ body = instruction.getBody();
+ else if (instruction.getBody() != null) {
+ if (!body.endsWith(";")) //$NON-NLS-1$
+ body += ';';
+ body += instruction.getBody();
+ }
+
+ String importAttribute = existingInstruction.getImportAttribute();
+ if (importAttribute == null || importAttribute.length() == 0)
+ importAttribute = instruction.getImportAttribute();
+ else if (instruction.getImportAttribute() != null) {
+ if (!importAttribute.endsWith(",")) //$NON-NLS-1$
+ importAttribute += ',';
+ importAttribute += instruction.getBody();
+ }
+ instruction = createTouchpointInstruction(body, importAttribute);
+ }
+ resultInstructions.put(key, instruction);
+ }
+ return createTouchpointData(resultInstructions);
+ }
+
public static ITouchpointInstruction createTouchpointInstruction(String body, String importAttribute) {
return new TouchpointInstruction(body, importAttribute);
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/Version.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/Version.java
deleted file mode 100644
index 9f47ece06..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/Version.java
+++ /dev/null
@@ -1,504 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009 Cloudsmith 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:
- * Cloudsmith Inc - initial API and implementation.
- *******************************************************************************/
-
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
-import org.eclipse.equinox.internal.p2.metadata.Messages;
-
-import org.eclipse.osgi.util.NLS;
-
-/**
- * <p>The Omni Version is composed of a vector of Comparable objects and a pad value. The pad
- * might be <code>null</code>. The vector can contain integers, strings, {@link VersionVector}
- * instances, or one of the special objects {@link VersionVector#MAX_VALUE MAX_VALUE},
- * {@link VersionVector#MAXS_VALUE MAXS_VALUE}, or {@link VersionVector#MIN_VALUE MIN_VALUE}.</p>
- *
- * <p>When two versions are compared, they are always considered padded to infinity by their
- * pad value or by {@link VersionVector#MIN_VALUE MIN_VALUE} in case the pad value is
- * <code>null</code>. The comparison is type sensitive so that:</p><pre>
- * MAX_VALUE &gt; Integer &gt; VersionVector &gt; MAXS_VALUE &gt; String &gt; MIN_VALUE<br/>
- * </pre>
- *
- * The class is signature compatible with {@link org.osgi.framework.Version} but attempts
- * to use it as such might render a {@link UnsupportedOperationException} in case the
- * vector holds incompatible values. The method {@link #isOSGiCompatible()} can be used
- * to test.
- *
- * @Immutable
- * @noextend This class is not intended to be subclassed by clients.
- */
-public class Version extends VersionVector {
- private static final Integer cache[] = new Integer[100];
-
- private static final char[] allowedOSGiChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-".toCharArray(); //$NON-NLS-1$
-
- public static final Integer ZERO_INT = new Integer(0);
-
- public static final Integer MAX_INT_OBJ = new Integer(Integer.MAX_VALUE);
-
- static {
- cache[0] = ZERO_INT;
- for (int i = 1; i < cache.length; i++)
- cache[i] = new Integer(i);
- }
-
- static Integer valueOf(int i) {
- try {
- return cache[i];
- } catch (ArrayIndexOutOfBoundsException e) {
- return (i == Integer.MAX_VALUE) ? MAX_INT_OBJ : new Integer(i);
- }
- }
-
- /**
- * The empty OSGi version "0.0.0". Equivalent to calling
- * <code>new Version(0,0,0)</code>.
- */
- public static final Version emptyVersion = new Version(0, 0, 0);
-
- /**
- * The version that is semantically greater then all other versions.
- */
- public static final Version MAX_VERSION = new Version("raw:MpM"); //$NON-NLS-1$
-
- /**
- * The version that is semantically less then all other versions.
- */
- public static final Version MIN_VERSION = new Version("raw:-M"); //$NON-NLS-1$
-
- private static final long serialVersionUID = 8202715438560849928L;
-
- /**
- * Creates an OSGi version identifier from the specified numerical components.
- *
- * <p>
- * The qualifier is set to the empty string.
- *
- * @param major Major component of the version identifier.
- * @param minor Minor component of the version identifier.
- * @param micro Micro component of the version identifier.
- * @throws IllegalArgumentException If the numerical components are
- * negative.
- */
- public static Version createOSGi(int major, int minor, int micro) {
- return createOSGi(major, minor, micro, null);
- }
-
- /**
- * Creates an OSGi version identifier from the specified components.
- *
- * @param major Major component of the version identifier.
- * @param minor Minor component of the version identifier.
- * @param micro Micro component of the version identifier.
- * @param qualifier Qualifier component of the version identifier. If
- * <code>null</code> is specified, then the qualifier will be set to
- * the empty string.
- * @throws IllegalArgumentException If the numerical components are negative
- * or the qualifier string is invalid.
- */
- public static Version createOSGi(int major, int minor, int micro, String qualifier) {
- // TODO: Eliminate duplicates
- return new Version(major, minor, micro, qualifier);
- }
-
- /**
- * Create an omni version from an OSGi <code>version</code>.
- * @param version The OSGi version. Can be <code>null</code>.
- * @return The created omni version
- */
- public static Version fromOSGiVersion(org.osgi.framework.Version version) {
- if (version == null)
- return null;
- if (version.equals(org.osgi.framework.Version.emptyVersion))
- return emptyVersion;
- return new Version(version.getMajor(), version.getMinor(), version.getMicro(), version.getQualifier());
- }
-
- /**
- * Parses a version identifier from the specified string.
- *
- * @param version String representation of the version identifier. Leading
- * and trailing whitespace will be ignored.
- * @return A <code>Version</code> object representing the version identifier
- * or <code>null</code> if <code>version</code> is <code>null</code> or
- * an empty string.
- * @throws IllegalArgumentException If <code>version</code> is improperly
- * formatted.
- */
- public static Version create(String version) {
- // TODO: Eliminate duplicates
- if (version != null) {
- Version v = new Version();
- if (VersionParser.parseInto(version, 0, version.length(), v))
- return v;
- }
- return null;
- }
-
- /**
- * Parses a version identifier from the specified string. This method is for backward
- * compatibility with OSGi and will return the OSGi {@link #emptyVersion} when
- * the provided string is empty or <code>null</code>.
- *
- * @param version String representation of the version identifier. Leading
- * and trailing whitespace will be ignored.
- * @return A <code>Version</code> object representing the version
- * identifier. If <code>version</code> is <code>null</code> or
- * the empty string then the OSGi <code>emptyVersion</code> will be
- * returned.
- * @throws IllegalArgumentException If <code>version</code> is improperly
- * formatted.
- * @see #create(String)
- */
- public static Version parseVersion(String version) {
- if (version == null || version.length() == 0 || "0.0.0".equals(version)) //$NON-NLS-1$
- return emptyVersion;
- Version v = create(version);
- return v == null ? emptyVersion : v;
- }
-
- /**
- * Convert <code>version</code> into its OSGi equivalent if possible.
- *
- * @param version The version to convert. Can be <code>null</code>
- * @return The converted version or <code>null</code> if the argument was <code>null</code>
- * @throws UnsupportedOperationException if the version could not be converted into an OSGi version
- */
- public static org.osgi.framework.Version toOSGiVersion(Version version) {
- if (version == null)
- return null;
- if (version.equals(emptyVersion))
- return org.osgi.framework.Version.emptyVersion;
- return new org.osgi.framework.Version(version.getMajor(), version.getMinor(), version.getMicro(), version.getQualifier());
- }
-
- /**
- * For exception messages only
- * @param i the index of the entry
- * @return the name of the entry
- */
- private static String getOSGiEntryName(int i) {
- String name = null;
- switch (i) {
- case 0 :
- name = "major"; //$NON-NLS-1$
- break;
- case 1 :
- name = "minor"; //$NON-NLS-1$
- break;
- case 2 :
- name = "micro"; //$NON-NLS-1$
- break;
- case 3 :
- name = "qualifier"; //$NON-NLS-1$
- }
- return name;
- }
-
- /**
- * The optional format
- */
- private VersionFormat format;
-
- /**
- * The optional original string
- */
- private String original;
-
- /**
- * Creates an OSGi version identifier from the specified numerical components.
- *
- * <p>
- * The qualifier is set to the empty string.
- *
- * @param major Major component of the version identifier.
- * @param minor Minor component of the version identifier.
- * @param micro Micro component of the version identifier.
- * @throws IllegalArgumentException If the numerical components are
- * negative.
- * @deprecated Use {@link #createOSGi(int, int, int)}. This constructor will not remain public
- */
- public Version(int major, int minor, int micro) {
- this(major, minor, micro, null);
- }
-
- /**
- * Creates an OSGi version identifier from the specified components.
- *
- * @param major Major component of the version identifier.
- * @param minor Minor component of the version identifier.
- * @param micro Micro component of the version identifier.
- * @param qualifier Qualifier component of the version identifier. If
- * <code>null</code> is specified, then the qualifier will be set to
- * the empty string.
- * @throws IllegalArgumentException If the numerical components are negative
- * or the qualifier string is invalid.
- * @deprecated Use {@link #createOSGi(int, int, int, String)}. This constructor will not remain public
- */
- public Version(int major, int minor, int micro, String qualifier) {
- if (qualifier != null && qualifier.length() == 0)
- qualifier = null;
- Comparable[] vector = new Comparable[qualifier == null ? 3 : 4];
- vector[0] = valueOf(major);
- vector[1] = valueOf(minor);
- vector[2] = valueOf(micro);
- if (qualifier != null)
- vector[3] = qualifier;
- init(vector, null, VersionFormat.OSGI_FORMAT, null);
- validateOSGI(true);
- }
-
- /**
- * Created a version identifier from the specified string.
- *
- * @param version String representation of the version identifier.
- * @throws IllegalArgumentException If <code>version</code> is improperly
- * formatted.
- * @deprecated Use {@link #parseVersion(String)}. This constructor will not remain public
- */
- public Version(String version) {
- if (!VersionParser.parseInto(version, 0, version.length(), this)) {
- // Version is OSGi empty
- init(new Comparable[] {ZERO_INT, ZERO_INT, ZERO_INT}, null, VersionFormat.OSGI_FORMAT, null);
- }
- }
-
- Version() {
- // Empty constructor
- }
-
- Version(Comparable[] array, Comparable padValue, VersionFormat format, String original) {
- init(array, padValue, format, original);
- }
-
- /**
- * Returns the optional format.
- */
- public VersionFormat getFormat() {
- return format;
- }
-
- /**
- * Returns the OSGi major component of this version identifier.
- *
- * @return The major component.
- * @throws UnsupportedOperationException if the first element in the
- * vector is not a number.
- * @see #isOSGiCompatible()
- */
- public int getMajor() {
- return getIntElement(0);
- }
-
- /**
- * Returns the OSGi micro component of this version identifier.
- *
- * @return The micro component.
- * @throws UnsupportedOperationException if the third element in the
- * vector is not a number.
- * @see #isOSGiCompatible()
- */
- public int getMicro() {
- return getIntElement(2);
- }
-
- /**
- * Returns the OSGi minor component of this version identifier.
- *
- * @return The minor component.
- * @throws UnsupportedOperationException if the second element in the
- * vector is not a number.
- * @see #isOSGiCompatible()
- */
- public int getMinor() {
- return getIntElement(1);
- }
-
- /**
- * Returns the <code>original</code> part of the string for this version
- * or <code>null</code> if no such part was provided when the version was
- * created. An OSGi type version will always return the OSGi string representation.
- *
- * @return The <code>original</code> part of the version string or
- * <code>null</code> if that part was missing.
- */
- public String getOriginal() {
- return original;
- }
-
- /**
- * Returns the OSGi qualifier component of this version identifier.
- *
- * @return The qualifier component or <code>null</code> if not set.
- * @throws UnsupportedOperationException if the fourth element in the
- * vector is set to something other then a string.
- * @see #isOSGiCompatible()
- */
- public String getQualifier() {
- Comparable[] vector = getVector();
- if (vector.length < 4)
- return null;
- if (!(vector[3] instanceof String))
- throw new UnsupportedOperationException();
- return (String) vector[3];
- }
-
- /**
- * Checks if this version is in compliance with the OSGi version spec.
- * @return A flag indicating whether the version is OSGi compatible or not.
- */
- public boolean isOSGiCompatible() {
- return format == VersionFormat.OSGI_FORMAT || validateOSGI(false);
- }
-
- /**
- * Appends the original for this version onto the <code>sb</code> StringBuffer
- * if present.
- * @param sb The buffer that will receive the raw string format
- * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped
- */
- public void originalToString(StringBuffer sb, boolean rangeSafe) {
- if (original != null) {
- if (rangeSafe) {
- // Escape all range delimiters while appending
- String s = original;
- int end = s.length();
- for (int idx = 0; idx < end; ++idx) {
- char c = s.charAt(idx);
- if (c == '\\' || c == '[' || c == '(' || c == ']' || c == ')' || c == ',' || c <= ' ')
- sb.append('\\');
- sb.append(c);
- }
- } else
- sb.append(original);
- }
- }
-
- /**
- * Appends the raw format for this version onto the <code>sb</code> StringBuffer.
- * @param sb The buffer that will receive the raw string format
- * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped
- */
- public void rawToString(StringBuffer sb, boolean rangeSafe) {
- super.toString(sb, rangeSafe);
- }
-
- /**
- * Appends the string representation of this version onto the
- * <code>sb</code> StringBuffer.
- * @param sb The buffer that will receive the version string
- */
- public void toString(StringBuffer sb) {
- if (format == VersionFormat.OSGI_FORMAT) {
- Comparable[] vector = getVector();
- sb.append(vector[0]);
- sb.append('.');
- sb.append(vector[1]);
- sb.append('.');
- sb.append(vector[2]);
- if (vector.length > 3) {
- sb.append('.');
- sb.append(vector[3]);
- }
- return;
- }
- sb.append(VersionParser.RAW_PREFIX);
- super.toString(sb, false);
- if (format != null || original != null) {
- sb.append('/');
- if (format != null)
- format.toString(sb);
- if (original != null) {
- sb.append(':');
- originalToString(sb, false);
- }
- }
- }
-
- void init(Comparable[] vec, Comparable pad, VersionFormat fmt, String orig) {
- init(vec, pad);
- format = fmt;
- //don't need to retain original for OSGi version
- if (fmt != VersionFormat.OSGI_FORMAT)
- original = orig;
- }
-
- private int getIntElement(int i) {
- Comparable[] vector = getVector();
- if (!(vector.length > i && vector[i] instanceof Integer))
- throw new UnsupportedOperationException();
- return ((Integer) vector[i]).intValue();
- }
-
- // Preserve singletons during deserialization
- private Object readResolve() {
- Version v = this;
- if (equals(MAX_VERSION))
- v = MAX_VERSION;
- else if (equals(MIN_VERSION))
- v = MIN_VERSION;
- else if (equals(emptyVersion))
- v = emptyVersion;
- else if (equals(VersionRange.OSGi_versionMax))
- v = VersionRange.OSGi_versionMax;
- else if (equals(VersionRange.OSGi_versionMin))
- v = VersionRange.OSGi_versionMin;
- return v;
- }
-
- boolean validateOSGI(boolean throwDetailed) {
- Comparable[] vector = getVector();
- if (vector.length < 3 || vector.length > 4) {
- if (throwDetailed)
- throw new IllegalArgumentException(NLS.bind(Messages.illegal_number_of_entries_0_in_osgi_1, valueOf(vector.length), this));
- return false;
- }
-
- if (getPad() != null) {
- if (throwDetailed)
- throw new IllegalArgumentException(NLS.bind(Messages.pad_not_allowed_in_osgi_0, this));
- return false;
- }
-
- for (int i = 0; i < 3; ++i) {
- Object e = vector[i];
- if (!(e instanceof Integer && ((Integer) e).intValue() >= 0)) {
- if (throwDetailed)
- throw new IllegalArgumentException(NLS.bind(Messages._0_is_not_a_positive_integer_in_osgi_1, getOSGiEntryName(i), this));
- return false;
- }
- }
- if (vector.length == 4) {
- Object e = vector[3];
- if (!(e instanceof String)) {
- if (throwDetailed)
- throw new IllegalArgumentException(NLS.bind(Messages._0_is_not_a_string_in_osgi_1, getOSGiEntryName(3), this));
- return false;
- }
-
- String s = (String) e;
- int idx = s.length();
- char[] allowed = allowedOSGiChars;
- int ctop = allowed.length;
- outer: while (--idx >= 0) {
- char c = s.charAt(idx);
- int cdx = ctop;
- while (--cdx >= 0)
- if (c == allowed[cdx])
- continue outer;
- if (throwDetailed)
- throw new IllegalArgumentException(NLS.bind(Messages._0_is_not_a_valid_qualifier_in_osgi_1, getOSGiEntryName(3), this));
- return false;
- }
- }
- return true;
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormatParser.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormatParser.java
deleted file mode 100644
index 903d15c92..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionFormatParser.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2009 Cloudsmith Inc. 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:
- * Cloudsmith Inc. - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
-import org.eclipse.equinox.internal.p2.metadata.Messages;
-
-import java.util.ArrayList;
-import java.util.List;
-import org.eclipse.osgi.util.NLS;
-
-/**
- * This is the Omni Version Format parser. It will parse a version format in string form
- * into a group of {@link VersionFormat.Fragment} elements. That group, wrapped in a
- * {@link VersionFormat}, becomes the parser for versions corresponding to the format.
- *
- * The class is not intended to included in a public API. Instead VersionFormats should
- * be created using {@link VersionFormat#parse(String)}
- *
- */
-class VersionFormatParser {
-
- static class Instructions {
- char[] characters = null;
- Comparable defaultValue = null;
- boolean ignore = false;
- boolean inverted = false;
- Comparable padValue = null;
- int rangeMax = Integer.MAX_VALUE;
- int rangeMin = 0;
- }
-
- static final VersionFormat.Qualifier EXACT_ONE_QUALIFIER = new VersionFormat.Qualifier(1, 1);
-
- static final VersionFormat.Qualifier ONE_OR_MANY_QUALIFIER = new VersionFormat.Qualifier(1, Integer.MAX_VALUE);
-
- static final VersionFormat.Qualifier ZERO_OR_MANY_QUALIFIER = new VersionFormat.Qualifier(0, Integer.MAX_VALUE);
-
- static final VersionFormat.Qualifier ZERO_OR_ONE_QUALIFIER = new VersionFormat.Qualifier(0, 1);
-
- private int current;
-
- private List currentList;
-
- private int eos;
-
- private String format;
-
- private int start;
-
- VersionFormat.Fragment compile(String fmt, int pos, int maxPos) throws FormatException {
- format = fmt;
- if (start >= maxPos)
- throw new FormatException(Messages.format_is_empty);
-
- start = pos;
- current = pos;
- eos = maxPos;
- currentList = new ArrayList();
- while (current < eos)
- parseFragment();
-
- VersionFormat.Fragment topFrag;
- switch (currentList.size()) {
- case 0 :
- throw new FormatException(Messages.format_is_empty);
- case 1 :
- VersionFormat.Fragment frag = (VersionFormat.Fragment) currentList.get(0);
- if (frag.isGroup()) {
- topFrag = frag;
- break;
- }
- // Fall through to default
- default :
- topFrag = VersionFormat.createGroupFragment(null, EXACT_ONE_QUALIFIER, (VersionFormat.Fragment[]) currentList.toArray(new VersionFormat.Fragment[currentList.size()]), false);
- }
- currentList = null;
- return topFrag;
- }
-
- private void assertChar(char expected) throws FormatException {
- if (current >= eos)
- throw formatException(NLS.bind(Messages.premature_end_of_format_expected_0, new String(new char[] {expected})));
-
- char c = format.charAt(current);
- if (c != expected)
- throw formatException(c, new String(new char[] {expected}));
- ++current;
- }
-
- private FormatException formatException(char found, String expected) {
- return formatException(new String(new char[] {found}), expected);
- }
-
- private FormatException formatException(String message) {
- return new FormatException(NLS.bind(Messages.syntax_error_in_version_format_0_1_2, new Object[] {format.substring(start, eos), new Integer(current), message}));
- }
-
- private FormatException formatException(String found, String expected) {
- return new FormatException(NLS.bind(Messages.syntax_error_in_version_format_0_1_found_2_expected_3, new Object[] {format.substring(start, eos), new Integer(current), found, expected}));
- }
-
- private FormatException illegalControlCharacter(char c) {
- return formatException(NLS.bind(Messages.illegal_character_encountered_ascii_0, Version.valueOf(c)));
- }
-
- private String parseAndConsiderEscapeUntil(char endChar) throws FormatException {
- StringBuffer sb = new StringBuffer();
- while (current < eos) {
- char c = format.charAt(current++);
- if (c == endChar)
- break;
-
- if (c < 32)
- throw illegalControlCharacter(c);
-
- if (c == '\\') {
- if (current == eos)
- throw formatException(Messages.EOS_after_escape);
- c = format.charAt(current++);
- if (c < 32)
- throw illegalControlCharacter(c);
- }
- sb.append(c);
- }
- return sb.toString();
- }
-
- private void parseAuto() throws FormatException {
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.padValue != null)
- throw formatException(Messages.auto_can_not_have_pad_value);
- }
- currentList.add(VersionFormat.createAutoFragment(ep, parseQualifier()));
- }
-
- private void parseBracketGroup() throws FormatException {
- List saveList = currentList;
- currentList = new ArrayList();
- while (current < eos && format.charAt(current) != ']')
- parseFragment();
-
- if (current == eos)
- throw formatException(NLS.bind(Messages.premature_end_of_format_expected_0, "]")); //$NON-NLS-1$
-
- ++current;
- VersionFormatParser.Instructions ep = parseProcessing();
- saveList.add(VersionFormat.createGroupFragment(ep, ZERO_OR_ONE_QUALIFIER, (VersionFormat.Fragment[]) currentList.toArray(new VersionFormat.Fragment[currentList.size()]), false));
- currentList = saveList;
- }
-
- private void parseCharacterGroup(VersionFormatParser.Instructions ep) throws FormatException {
- assertChar('[');
-
- StringBuffer sb = new StringBuffer();
- outer: for (; current < eos; ++current) {
- char c = format.charAt(current);
- switch (c) {
- case '\\' :
- if (current + 1 < eos) {
- sb.append(format.charAt(++current));
- continue;
- }
- throw formatException(Messages.premature_end_of_format);
- case '^' :
- if (sb.length() == 0)
- ep.inverted = true;
- else
- sb.append(c);
- continue;
- case ']' :
- break outer;
- case '-' :
- if (sb.length() > 0 && current + 1 < eos) {
- char rangeEnd = format.charAt(++current);
- if (rangeEnd == ']') {
- // Use dash verbatim when last in range
- sb.append(c);
- break outer;
- }
-
- char rangeStart = sb.charAt(sb.length() - 1);
- if (rangeEnd < rangeStart)
- throw formatException(Messages.negative_character_range);
- while (++rangeStart <= rangeEnd)
- sb.append(rangeStart);
- continue;
- }
- // Fall through to default
- default :
- if (c < 32)
- throw illegalControlCharacter(c);
- sb.append(c);
- }
- }
- assertChar(']');
- int top = sb.length();
- char[] chars = new char[top];
- sb.getChars(0, top, chars, 0);
- ep.characters = chars;
- }
-
- private void parseDelimiter() throws FormatException {
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.rangeMin != 0 || ep.rangeMax != Integer.MAX_VALUE)
- throw formatException(Messages.delimiter_can_not_have_range);
- if (ep.ignore)
- throw formatException(Messages.delimiter_can_not_be_ignored);
- if (ep.defaultValue != null)
- throw formatException(Messages.delimiter_can_not_have_default_value);
- if (ep.padValue != null)
- throw formatException(Messages.delimiter_can_not_have_pad_value);
- }
- currentList.add(VersionFormat.createDelimiterFragment(ep, parseQualifier()));
- }
-
- private void parseFragment() throws FormatException {
- if (current == eos)
- throw formatException(Messages.premature_end_of_format);
- char c = format.charAt(current++);
- switch (c) {
- case '(' :
- parseGroup(false);
- break;
- case '<' :
- parseGroup(true);
- break;
- case '[' :
- parseBracketGroup();
- break;
- case 'a' :
- parseAuto();
- break;
- case 'r' :
- parseRaw();
- break;
- case 'n' :
- parseNumber(false);
- break;
- case 'N' :
- parseNumber(true);
- break;
- case 's' :
- parseString(false);
- break;
- case 'S' :
- parseString(true);
- break;
- case 'd' :
- parseDelimiter();
- break;
- case 'q' :
- parseQuotedString();
- break;
- case 'p' :
- parsePad();
- break;
- default :
- parseLiteral(c);
- }
- }
-
- private void parseGroup(boolean array) throws FormatException {
- List saveList = currentList;
- currentList = new ArrayList();
- char expectedEnd = array ? '>' : ')';
- while (current < eos && format.charAt(current) != expectedEnd)
- parseFragment();
- assertChar(expectedEnd);
-
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.characters != null)
- throw formatException(Messages.array_can_not_have_character_group);
- if (ep.rangeMax != Integer.MAX_VALUE && ep.padValue != null) {
- throw formatException(Messages.cannot_combine_range_upper_bound_with_pad_value);
- }
- }
-
- if (currentList.isEmpty())
- throw formatException(array ? Messages.array_can_not_be_empty : Messages.group_can_not_be_empty);
- saveList.add(VersionFormat.createGroupFragment(ep, parseQualifier(), (VersionFormat.Fragment[]) currentList.toArray(new VersionFormat.Fragment[currentList.size()]), array));
- currentList = saveList;
- }
-
- private int parseIntegerLiteral() throws FormatException {
- if (current == eos)
- throw formatException(NLS.bind(Messages.premature_end_of_format_expected_0, "<integer>")); //$NON-NLS-1$
-
- char c = format.charAt(current);
- if (!VersionParser.isDigit(c))
- throw formatException(c, "<integer>"); //$NON-NLS-1$
-
- int value = c - '0';
- while (++current < eos) {
- c = format.charAt(current);
- if (!VersionParser.isDigit(c))
- break;
- value *= 10;
- value += (c - '0');
- }
- return value;
- }
-
- private void parseLiteral(char c) throws FormatException {
- String value;
- switch (c) {
- case '\'' :
- value = parseAndConsiderEscapeUntil(c);
- break;
- case ')' :
- case ']' :
- case '{' :
- case '}' :
- case '?' :
- case '*' :
- throw formatException(c, "<literal>"); //$NON-NLS-1$
- default :
- if (VersionParser.isLetterOrDigit(c))
- throw formatException(c, "<literal>"); //$NON-NLS-1$
-
- if (c < 32)
- throw illegalControlCharacter(c);
-
- if (c == '\\') {
- if (current == eos)
- throw formatException(Messages.EOS_after_escape);
- c = format.charAt(current++);
- if (c < 32)
- throw illegalControlCharacter(c);
- }
- value = new String(new char[] {c});
- }
- currentList.add(VersionFormat.createLiteralFragment(parseQualifier(), value));
- }
-
- private int[] parseMinMax() throws FormatException {
-
- int max = Integer.MAX_VALUE;
- ++current;
- int min = parseIntegerLiteral();
- char c = format.charAt(current);
- if (c == '}') {
- max = min;
- if (max == 0)
- throw formatException(Messages.range_max_cannot_be_zero);
- ++current;
- } else if (c == ',' && current + 1 < eos) {
- if (format.charAt(++current) != '}') {
- max = parseIntegerLiteral();
- if (max == 0)
- throw formatException(Messages.range_max_cannot_be_zero);
- if (max < min)
- throw formatException(Messages.range_max_cannot_be_less_then_range_min);
- }
- assertChar('}');
- } else
- throw formatException(c, "},"); //$NON-NLS-1$
- return new int[] {min, max};
- }
-
- private void parseNumber(boolean signed) throws FormatException {
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.padValue != null)
- throw formatException(Messages.number_can_not_have_pad_value);
- }
- currentList.add(VersionFormat.createNumberFragment(ep, parseQualifier(), signed));
- }
-
- private void parsePad() throws FormatException {
- currentList.add(VersionFormat.createPadFragment(parseQualifier()));
- }
-
- private VersionFormatParser.Instructions parseProcessing() throws FormatException {
- if (current >= eos)
- return null;
-
- char c = format.charAt(current);
- if (c != '=')
- return null;
-
- VersionFormatParser.Instructions ep = new VersionFormatParser.Instructions();
- do {
- current++;
- parseProcessingInstruction(ep);
- } while (current < eos && format.charAt(current) == '=');
- return ep;
- }
-
- private void parseProcessingInstruction(VersionFormatParser.Instructions processing) throws FormatException {
- if (current == eos)
- throw formatException(Messages.premature_end_of_format);
-
- char c = format.charAt(current);
- if (c == 'p') {
- // =pad(<raw-element>);
- //
- if (processing.padValue != null)
- throw formatException(Messages.pad_defined_more_then_once);
- if (processing.ignore)
- throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
- ++current;
- processing.padValue = parseRawElement();
- } else if (c == '!') {
- // =ignore;
- //
- if (processing.ignore)
- throw formatException(Messages.ignore_defined_more_then_once);
- if (processing.padValue != null || processing.characters != null || processing.rangeMin != 0 || processing.rangeMax != Integer.MAX_VALUE || processing.defaultValue != null)
- throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
- ++current;
- processing.ignore = true;
- } else if (c == '[') {
- // =[<character group];
- //
- if (processing.characters != null)
- throw formatException(Messages.character_group_defined_more_then_once);
- if (processing.ignore)
- throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
- parseCharacterGroup(processing);
- } else if (c == '{') {
- // ={min,max};
- //
- if (processing.rangeMin != 0 || processing.rangeMax != Integer.MAX_VALUE)
- throw formatException(Messages.range_defined_more_then_once);
- if (processing.ignore)
- throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
- int[] minMax = parseMinMax();
- processing.rangeMin = minMax[0];
- processing.rangeMax = minMax[1];
- } else {
- // =<raw-element>;
- if (processing.defaultValue != null)
- throw formatException(Messages.default_defined_more_then_once);
- if (processing.ignore)
- throw formatException(Messages.cannot_combine_ignore_with_other_instruction);
- processing.defaultValue = parseRawElement();
- }
- assertChar(';');
- }
-
- private VersionFormat.Qualifier parseQualifier() throws FormatException {
- if (current >= eos)
- return EXACT_ONE_QUALIFIER;
-
- char c = format.charAt(current);
- if (c == '?') {
- ++current;
- return ZERO_OR_ONE_QUALIFIER;
- }
-
- if (c == '*') {
- ++current;
- return ZERO_OR_MANY_QUALIFIER;
- }
-
- if (c == '+') {
- ++current;
- return ONE_OR_MANY_QUALIFIER;
- }
-
- if (c != '{')
- return EXACT_ONE_QUALIFIER;
-
- int[] minMax = parseMinMax();
- int min = minMax[0];
- int max = minMax[1];
-
- // Use singletons for commonly used ranges
- //
- if (min == 0) {
- if (max == 1)
- return ZERO_OR_ONE_QUALIFIER;
- if (max == Integer.MAX_VALUE)
- return ZERO_OR_MANY_QUALIFIER;
- } else if (min == 1) {
- if (max == 1)
- return EXACT_ONE_QUALIFIER;
- if (max == Integer.MAX_VALUE)
- return ONE_OR_MANY_QUALIFIER;
- }
- return new VersionFormat.Qualifier(min, max);
- }
-
- private void parseQuotedString() throws FormatException {
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.padValue != null)
- throw formatException(Messages.string_can_not_have_pad_value);
- }
- currentList.add(VersionFormat.createQuotedFragment(ep, parseQualifier()));
- }
-
- private void parseRaw() throws FormatException {
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.padValue != null)
- throw formatException(Messages.raw_element_can_not_have_pad_value);
- }
- currentList.add(VersionFormat.createRawFragment(ep, parseQualifier()));
- }
-
- private Comparable parseRawElement() throws FormatException {
- int[] position = new int[] {current};
- Comparable v = VersionParser.parseRawElement(format, position, eos);
- if (v == null)
- throw new FormatException(NLS.bind(Messages.raw_element_expected_0, format));
- current = position[0];
- return v;
- }
-
- private void parseString(boolean unlimited) throws FormatException {
- VersionFormatParser.Instructions ep = parseProcessing();
- if (ep != null) {
- if (ep.padValue != null)
- throw formatException(Messages.string_can_not_have_pad_value);
- }
- currentList.add(VersionFormat.createStringFragment(ep, parseQualifier(), unlimited));
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CapabilityQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CapabilityQuery.java
deleted file mode 100644
index 4336989a9..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CapabilityQuery.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2007, 2009 IBM Corporation 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:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
-import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
-import org.eclipse.equinox.internal.provisional.p2.metadata.IRequiredCapability;
-
-/**
- * A query that searches for {@link IInstallableUnit} instances that provide
- * capabilities that match one or more required capabilities.
- */
-public class CapabilityQuery extends MatchQuery {
- private IRequiredCapability[] required;
-
- /**
- * Creates a new query on the given required capability.
- * @param required The required capability
- */
- public CapabilityQuery(IRequiredCapability required) {
- this.required = new IRequiredCapability[] {required};
- }
-
- /**
- * Creates a new query on the given required capabilities. The installable
- * unit must provide capabilities that match all of the given required capabilities
- * for this query to be satisfied.
- * @param required The required capabilities
- */
- public CapabilityQuery(IRequiredCapability[] required) {
- this.required = required;
- }
-
- /**
- * Returns the required capability that this query is matching against.
- * @return the required capability that this query is matching against.
- */
- public IRequiredCapability[] getRequiredCapabilities() {
- return required;
- }
-
- /* (non-Javadoc)
- * @see org.eclipse.equinox.p2.query2.Query#isMatch(java.lang.Object)
- */
- public boolean isMatch(Object object) {
- if (!(object instanceof IInstallableUnit))
- return false;
- IInstallableUnit candidate = (IInstallableUnit) object;
- for (int i = 0; i < required.length; i++)
- if (!candidate.satisfies(required[i]))
- return false;
- return true;
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/LatestIUVersionQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/LatestIUVersionQuery.java
deleted file mode 100644
index 098642d64..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/LatestIUVersionQuery.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
-* Copyright (c) 2009 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
-*
-* Contributors:
-* EclipseSource - initial API and implementation
-******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
-import java.util.*;
-import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
-
-/**
- * This query returns the latest version for each unique IU Id.
- * All other elements are discarded.
- */
-public class LatestIUVersionQuery extends ContextQuery {
-
- /**
- * Performs the LatestIUVersionQuery
- */
- public Collector perform(Iterator iterator, Collector result) {
- HashMap greatestIUVersion = new HashMap();
- while (iterator.hasNext()) {
- Object next = iterator.next();
-
- if (!(next instanceof IInstallableUnit))
- // Don't accept things if they are not IUs
- continue;
- IInstallableUnit iu = (IInstallableUnit) next;
- if (greatestIUVersion.containsKey(iu.getId())) {
- IInstallableUnit currentIU = (IInstallableUnit) greatestIUVersion.get(iu.getId());
- if (currentIU.getVersion().compareTo(iu.getVersion()) < 0)
- greatestIUVersion.put(iu.getId(), iu);
- } else
- greatestIUVersion.put(iu.getId(), iu);
- }
-
- Collection values = greatestIUVersion.values();
- Iterator valuesIterator = values.iterator();
- boolean continueGather = true;
-
- while (valuesIterator.hasNext() && continueGather) {
- IInstallableUnit nextIU = (IInstallableUnit) valuesIterator.next();
- continueGather = result.accept(nextIU);
- }
- return result;
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/UpdateQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/UpdateQuery.java
deleted file mode 100644
index 6774bcbfb..000000000
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/UpdateQuery.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2008, 2009 IBM Corporation 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:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
-
-public class UpdateQuery extends MatchQuery {
- private IInstallableUnit updateFrom;
-
- public UpdateQuery(IInstallableUnit updateFrom) {
- this.updateFrom = updateFrom;
- }
-
- public boolean isMatch(Object obj) {
- if (!(obj instanceof IInstallableUnit))
- return false;
- if (obj instanceof IInstallableUnitPatch) {
- IInstallableUnitPatch potentialPatch = (IInstallableUnitPatch) obj;
- IRequiredCapability lifeCycle = potentialPatch.getLifeCycle();
- if (lifeCycle == null)
- return false;
- return updateFrom.satisfies(lifeCycle);
- }
- IInstallableUnit candidate = (IInstallableUnit) obj;
- IUpdateDescriptor descriptor = candidate.getUpdateDescriptor();
- if (descriptor != null && descriptor.isUpdateOf(updateFrom) && updateFrom.getVersion().compareTo(candidate.getVersion()) < 0)
- return true;
- return false;
- }
-}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IArtifactKey.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IArtifactKey.java
index ee07d8176..fdcba7a80 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IArtifactKey.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IArtifactKey.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2009 IBM Corporation and others.
+ * Copyright (c) 2007, 2010 IBM Corporation 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
@@ -8,21 +8,21 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
+package org.eclipse.equinox.p2.metadata;
/**
- * Provide standardized artifact information to uniquely identify the
+ * Provide standardised artifact information to uniquely identify the
* corresponding bytes (perhaps not stored as a file).
* <p>
* Artifact keys represent both a unique opaque identifier as well as structured
- * and standardized pieces of information.
+ * and standardised pieces of information.
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
-public interface IArtifactKey {
+public interface IArtifactKey extends IVersionedId {
/**
* Returns the classifier for this artifact key. The returned value can be empty.
@@ -31,8 +31,8 @@ public interface IArtifactKey {
public String getClassifier();
/**
- * Returns the id for this artifact key. The returned value can be empty.
- * @return the classifier segment of the key.
+ * Returns the id for this artifact key.
+ * @return the id segment of the key.
*/
public String getId();
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ICopyright.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ICopyright.java
index 990b87432..d8ad2bd43 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ICopyright.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ICopyright.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2008 EclipseSource and others. All rights reserved. This
+* Copyright (c) 2008, 2009 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
@@ -7,7 +7,7 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
import java.net.URI;
@@ -18,6 +18,7 @@ import java.net.URI;
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface ICopyright {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnit.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnit.java
new file mode 100644
index 000000000..fbc39cbb4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnit.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ * Genuitec, LLC - added license support
+ * EclipseSource - ongoing development
+ ******************************************************************************/
+package org.eclipse.equinox.p2.metadata;
+
+import java.util.*;
+import org.osgi.framework.Filter;
+
+/**
+ * An installable unit represents an atomic, indivisible unit of installable functionality
+ * in a provisioned system. Everything that can be installed or uninstalled in a system,
+ * including both concrete artifacts and instructions describing steps to be performed
+ * during install, must be expressed as one or more installable units. Thus the set of
+ * installable units present in a system, together with the existing environment
+ * (operating system, etc), completely describes the initial installed state of that system.
+ * <p>
+ * Installable units may have dependencies on functionality provided by other installable
+ * units, such that the unit cannot be installed unless some other installable unit
+ * is present in the installed system that provides a matching capability. Such
+ * dependencies are referred to as <i>required capabilities</i>. Conversely,
+ * installable units may declared <i>provided capabilities</i>, describing the
+ * capabilities that they make available to other units in the system. Note the
+ * weak coupling at work here: installable units never directly depend on each other,
+ * but instead depend on abstract capabilities that any other installable unit may provide.
+ * </p>
+ *
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
+ */
+public interface IInstallableUnit extends IVersionedId, Comparable<IInstallableUnit> {
+
+ /**
+ * A capability namespace representing a particular InstallableUnit by id.
+ * Each InstallableUnit automatically provides a capability in this namespace representing
+ * itself, and other InstallableUnits can require such a capability to state that they
+ * require a particular InstallableUnit to be present.
+ *
+ * @see IInstallableUnit#getId()
+ */
+ public static final String NAMESPACE_IU_ID = "org.eclipse.equinox.p2.iu"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.partial.iu"</code>) for a
+ * boolean property indicating the IU is generated from incomplete information and
+ * should be replaced by the complete IU if available.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_PARTIAL_IU = "org.eclipse.equinox.p2.partial.iu"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.contact"</code>) for a
+ * String property containing a contact address where problems can be reported,
+ * such as an email address.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_CONTACT = "org.eclipse.equinox.p2.contact"; //$NON-NLS-1$
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.description"</code>) for a
+ * String property containing a human-readable description of the installable unit.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_DESCRIPTION = "org.eclipse.equinox.p2.description"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.description.url"</code>) for a
+ * String property containing a URL to the description of the installable unit.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_DESCRIPTION_URL = "org.eclipse.equinox.p2.description.url"; //$NON-NLS-1$
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.doc.url"</code>) for a
+ * String property containing a URL for documentation about the installable unit.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_DOC_URL = "org.eclipse.equinox.p2.doc.url"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.bundle.localization"</code>) for a String
+ * property containing the bundle localization property file name
+ */
+ public static final String PROP_BUNDLE_LOCALIZATION = "org.eclipse.equinox.p2.bundle.localization"; //$NON-NLS-1$
+
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.name"</code>) for a
+ * String property containing a human-readable name for the installable unit.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_NAME = "org.eclipse.equinox.p2.name"; //$NON-NLS-1$
+ /**
+ * A property key (value <code>"org.eclipse.equinox.p2.provider"</code>) for a
+ * String property containing information about the vendor or provider of the
+ * installable unit.
+ *
+ * @see #getProperty(String)
+ */
+ public static final String PROP_PROVIDER = "org.eclipse.equinox.p2.provider"; //$NON-NLS-1$
+
+ /**
+ * Returns the collection of artifacts associated with this installable unit.
+ * Installing this unit into a system will cause these artifacts to be fetched from
+ * a repository and applied to the installed system. Uninstalling this unit
+ * will cause these artifacts to be removed from the system.
+ *
+ * @return The artifacts associated with this installable unit
+ */
+ public Collection<IArtifactKey> getArtifacts();
+
+ /**
+ * Returns the filter on this installable unit. The filter is matched against
+ * the properties of the environment the unit is installed into. An installable
+ * unit will not be installed if it has a filter condition that is not satisfied by the
+ * properties of the environment.
+ *
+ * @return The installation filter for this unit, or <code>null</code>
+ */
+ public Filter getFilter();
+
+ /**
+ * Returns the fragments that have been bound to this installable unit, or
+ * <code>null</code> if this unit is not resolved.
+ *
+ * @see #isResolved()
+ * @return The fragments bound to this installable unit, or <code>null</code>
+ */
+ public List<IInstallableUnitFragment> getFragments();
+
+ /**
+ * Returns an <i>unmodifiable copy</i> of the properties
+ * associated with the installable unit.
+ *
+ * @return an <i>unmodifiable copy</i> of the properties of this installable unit.
+ */
+ public Map<String, String> getProperties();
+
+ /**
+ * Returns the untranslated property of this installable unit associated with the given key.
+ * Returns <code>null</code> if no such property is defined.
+ * <p>
+ * If the property value has been externalized, this method will return a string containing
+ * the translation key rather than a human-readable string. For this reason, clients
+ * wishing to obtain the value for a property that is typically translated should use
+ * {@link #getProperty(String, String)} instead.
+ * </p>
+ *
+ * @param key The property key to retrieve a property value for
+ * @return the property that applies to this installable unit or <code>null</code>
+ */
+ public String getProperty(String key);
+
+ /**
+ * Returns the property of this installable unit associated with the given key.
+ * Returns <code>null</code> if no such property is defined or no applicable
+ * translation is available.
+ *
+ * @param key The property key to retrieve a property value for
+ * @param locale The locale to translate the property for, or null to use the current locale.
+ * @return the property that applies to this installable unit or <code>null</code>
+ */
+ public String getProperty(String key, String locale);
+
+ /**
+ * Returns the collection of capabilities provided by this installable unit.
+ *
+ * @return The collection of capabilities provided by this installable unit.
+ */
+ public Collection<IProvidedCapability> getProvidedCapabilities();
+
+ public Collection<IRequirement> getRequiredCapabilities();
+
+ public Collection<IRequirement> getMetaRequiredCapabilities();
+
+ public List<ITouchpointData> getTouchpointData();
+
+ public ITouchpointType getTouchpointType();
+
+ /**
+ * Returns whether this installable unit has been resolved. A resolved
+ * installable unit represents the union of an installable unit and some
+ * fragments.
+ *
+ * @see #getFragments()
+ * @see #unresolved()
+ * @return <code>true</code> if this installable unit is resolved, and
+ * <code>false</code> otherwise.
+ */
+ public boolean isResolved();
+
+ /**
+ * Returns whether this installable unit is a singleton. Only one singleton
+ * installable unit with a given id is allowed to exist in a given installed system.
+ * Attempting to install multiple versions of a singleton will fail.
+ * @return <code>true</code> if this unit is a singleton, and <code>false</code> otherwise
+ */
+ public boolean isSingleton();
+
+ /**
+ * Returns whether this unit has a provided capability that satisfies the given
+ * requirement.
+ * @return <code>true</code> if this unit satisfies the given requirement, and <code>false</code> otherwise.
+ */
+ public boolean satisfies(IRequirement candidate);
+
+ /**
+ * Returns the unresolved equivalent of this installable unit. If this unit is
+ * already unresolved, this method returns the receiver. Otherwise, this
+ * method returns an installable unit with the same id and version, but without
+ * any fragments attached.
+ *
+ * @see #getFragments()
+ * @see #isResolved()
+ * @return The unresolved equivalent of this unit
+ */
+ public IInstallableUnit unresolved();
+
+ /**
+ * Returns information about what this installable unit is an update of.
+ * @return The lineage information about the installable unit
+ */
+ public IUpdateDescriptor getUpdateDescriptor();
+
+ /**
+ * Returns the untranslated licenses that apply to this installable unit.
+ * <p>
+ * If the license text has been externalized, this method will return strings containing
+ * the translation keys rather than human-readable strings. For this reason, clients
+ * wishing to obtain a license for display to an end user should use {@link #getLicenses(String)}
+ * instead.
+ * </p>
+ * @return the licenses that apply to this installable unit
+ */
+ public Collection<ILicense> getLicenses();
+
+ /**
+ * Returns the licenses that apply to this installable unit. Any translation of the
+ * licenses for the given locale will be applied. Returns an empty array if this
+ * unit has no licenses, or if the available licenses are externalized and do not
+ * have translations available for the given locale.
+ *
+ * @param locale The locale to translate the license for, or null to use the current locale.
+ * @return the translated licenses that apply to this installable unit
+ */
+ public ILicense[] getLicenses(String locale);
+
+ /**
+ * Returns the untranslated copyright that applies to this installable unit.
+ * <p>
+ * If the copyright text has been externalized, this method will return strings containing
+ * the translation keys rather than human-readable strings. For this reason, clients
+ * wishing to obtain a copyright for display to an end user should use {@link #getCopyright(String)}
+ * instead.
+ * </p>
+ * @return the copyright that applies to this installable unit or <code>null</code>
+ */
+ public ICopyright getCopyright();
+
+ /**
+ * Returns the copyright that applies to this installable unit. Any translation of the
+ * copyright for the given locale will be applied. Returns <code>null</code> if this
+ * unit has no copyright, or if the copyright is externalized and no translations are
+ * available for the given locale.
+ *
+ * @param locale The locale to translate the copyright for, or null to use the current locale.
+ * @return the copyright that applies to this installable unit or <code>null</code>
+ */
+ public ICopyright getCopyright(String locale);
+
+ /**
+ * Returns whether this InstallableUnit is equal to the given object.
+ *
+ * This method returns <i>true</i> if:
+ * <ul>
+ * <li> Both this object and the given object are of type IInstallableUnit
+ * <li> The result of <b>getId()</b> on both objects are equal
+ * <li> The result of <b>getVersion()</b> on both objects are equal
+ * </ul>
+ */
+ public boolean equals(Object obj);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnitFragment.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnitFragment.java
index a803f63a0..d9c93283d 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnitFragment.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnitFragment.java
@@ -8,12 +8,13 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
/**
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface IInstallableUnitFragment extends IInstallableUnit {
- public IRequiredCapability[] getHost();
+ public IRequirement[] getHost();
} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnitPatch.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnitPatch.java
index f67c7127d..600a9f325 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IInstallableUnitPatch.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IInstallableUnitPatch.java
@@ -8,7 +8,9 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
+
+import java.util.List;
/**
* An installable unit patch is an installable unit that alters the required capabilities of another
@@ -16,6 +18,7 @@ package org.eclipse.equinox.internal.provisional.p2.metadata;
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface IInstallableUnitPatch extends IInstallableUnit {
/**
@@ -30,13 +33,13 @@ public interface IInstallableUnitPatch extends IInstallableUnit {
* a scope of [[r1, r2, r3], [r4, r5]] will match any unit whose provided capabilities
* satisfy the expression ((r1 ^ r2 ^ r3) | (r4 ^ r5)).
*/
- IRequiredCapability[][] getApplicabilityScope();
+ IRequirement[][] getApplicabilityScope();
/**
* Returns the requirement changes imposed by the patch.
* @return The patch requirement changes.
*/
- IRequirementChange[] getRequirementsChange();
+ List<IRequirementChange> getRequirementsChange();
/**
* Returns the required capability that defines the lifecycle of this patch. The
@@ -44,5 +47,5 @@ public interface IInstallableUnitPatch extends IInstallableUnit {
* is satisfied by some IU in the profile. If a future provisioning operation causes
* the requirement to no longer be satisfied, the patch will be uninstalled.
*/
- IRequiredCapability getLifeCycle();
+ IRequirement getLifeCycle();
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ILicense.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ILicense.java
index 99916dec8..5d613ad9b 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ILicense.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ILicense.java
@@ -8,9 +8,8 @@
* EclipseSource - initial API and implementation
* IBM - ongoing development
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
-import java.math.BigInteger;
import java.net.URI;
/**
@@ -20,6 +19,7 @@ import java.net.URI;
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface ILicense {
@@ -40,11 +40,9 @@ public interface ILicense {
* Returns the message digest of the license body. The digest is calculated on a normalized
* version of the license where all whitespace has been reduced to one space.
*
- * Any SPI must maintain the same semantics as:
- * <code>{@link org.eclipse.equinox.internal.provisional.p2.metadata.ILicense#getDigest()}</code>
* @return the message digest as a <code>BigInteger</code>, never <code>null</code>
*/
- public BigInteger getDigest();
+ public String getUUID();
/**
* Returns whether this license is equal to the given object.
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IProvidedCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IProvidedCapability.java
index 971d2f6f8..0496e8925 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IProvidedCapability.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IProvidedCapability.java
@@ -8,15 +8,14 @@
* EclipseSource - initial API and implementation
* IBM - ongoing development
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
-
+package org.eclipse.equinox.p2.metadata;
/**
* Describes a capability as exposed or required by an installable unit
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface IProvidedCapability {
@@ -27,25 +26,6 @@ public interface IProvidedCapability {
public Version getVersion();
/**
- * Returns whether this provided capability satisfies the given required capability.
- * @return <code>true</code> if this capability satisfies the given required
- * capability, and <code>false</code> otherwise.
- *
- * This method must maintain the following semantics:
- * <ul>
- * <li> If the provided capability and the candidate have different names,
- * return false
- * <li> If the provided capability and the candidate have different namespaces.
- * return false
- * <li> If the candidate's version range includes the provided capability's
- * version, return true
- * <li> otherwise, return false
- * </ul>
- *
- */
- public boolean satisfies(IRequiredCapability candidate);
-
- /**
* Returns whether this provided capability is equal to the given object.
*
* This method returns <i>true</i> if:
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirement.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirement.java
new file mode 100644
index 000000000..cc866e2fa
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirement.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+* Copyright (c) 2009 IBM 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:
+* IBM - initial API and implementation
+******************************************************************************/
+package org.eclipse.equinox.p2.metadata;
+
+import org.eclipse.equinox.p2.metadata.expression.IMatchExpression;
+import org.osgi.framework.Filter;
+
+/**
+ * @noimplement This interface is not intended to be implemented by clients.
+ * @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
+ */
+public interface IRequirement {
+
+ int getMin();
+
+ int getMax();
+
+ Filter getFilter();
+
+ /**
+ * Returns a boolean match expression that will return true for any
+ * {@link IInstallableUnit} that matches the requirement.
+ * @return A boolean match expression for installable unit matching.
+ */
+ IMatchExpression<IInstallableUnit> getMatches();
+
+ boolean isMatch(IInstallableUnit iu);
+
+ boolean isGreedy();
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequirementChange.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirementChange.java
index 59578f7da..16c21098b 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IRequirementChange.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IRequirementChange.java
@@ -7,11 +7,14 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
+
+import org.eclipse.equinox.internal.p2.metadata.IRequiredCapability;
/**
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface IRequirementChange {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointData.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointData.java
index c1ea83b1a..3ab8b8c93 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointData.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointData.java
@@ -7,9 +7,10 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
import java.util.Map;
+import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory;
/**
* ITouchpoint data instances contain the additional information needed by a touchpoint
@@ -21,6 +22,7 @@ import java.util.Map;
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface ITouchpointData {
@@ -35,7 +37,7 @@ public interface ITouchpointData {
*
* @return the touchpoint instructions
*/
- public Map getInstructions();
+ public Map<String, ITouchpointInstruction> getInstructions();
/**
* Returns whether this TouchpointData is equal to the given object.
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointInstruction.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointInstruction.java
index e2322f5e7..e7c9b49b9 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointInstruction.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointInstruction.java
@@ -7,7 +7,7 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
/**
* A touchpoint instruction contains either a sequence of instruction statements
@@ -43,6 +43,7 @@ package org.eclipse.equinox.internal.provisional.p2.metadata;
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface ITouchpointInstruction {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointType.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointType.java
index f003b91d9..02218f760 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/ITouchpointType.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ITouchpointType.java
@@ -7,8 +7,7 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
+package org.eclipse.equinox.p2.metadata;
/**
@@ -17,6 +16,7 @@ package org.eclipse.equinox.internal.provisional.p2.metadata;
*
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface ITouchpointType {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IUpdateDescriptor.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IUpdateDescriptor.java
index 268ff247c..047eb9f43 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IUpdateDescriptor.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IUpdateDescriptor.java
@@ -8,13 +8,13 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
+package org.eclipse.equinox.p2.metadata;
/**
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
public interface IUpdateDescriptor {
public final int NORMAL = 0;
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionFormat.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionFormat.java
new file mode 100644
index 000000000..e87df3624
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionFormat.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata;
+
+/**
+ * <p>The IVersionFormat represents the Omni Version Format in compiled form. It
+ * is also a parser for versions of that format.</p>
+ * <p>An instance of IVersionFormat is immutable and thus thread safe. The parser
+ * does not maintain any state.</p>
+ * @since 2.0
+ */
+public interface IVersionFormat {
+
+ /**
+ * The string that by default will be interpreted as the logical max string when parsing
+ * optional elements of type string and a default that is the empty string (i.e. OSGi)
+ */
+ static final String DEFAULT_MAX_STRING_TRANSLATION = "zzz"; //$NON-NLS-1$
+
+ /**
+ * The string that by default will be interpreted as the logical min string when parsing
+ * optional elements of type string and a default that is the max string (i.e. Maven triplets)
+ */
+ static final String DEFAULT_MIN_STRING_TRANSLATION = "-"; //$NON-NLS-1$
+
+ /**
+ * Appends the string representation of this compiled format to
+ * the given StringBuffer.
+ * @param sb The buffer that will receive the string representation
+ */
+ void toString(StringBuffer sb);
+
+ /**
+ * Parse the given version string.
+ * @param version The version string to parse.
+ * @return A created version.
+ * @throws IllegalArgumentException If the version string could not be parsed.
+ */
+ Version parse(String version);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IVersionedId.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionedId.java
index 77184d3c7..eb8aec341 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/IVersionedId.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/IVersionedId.java
@@ -7,11 +7,14 @@
*
* Contributors:
* Cloudsmith Inc. - initial API and implementation
+ * IBM - Ongoing development
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
+
/**
* An interface representing a (id,version) pair.
+ * @since 2.0
*/
public interface IVersionedId {
/**
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/Version.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/Version.java
new file mode 100644
index 000000000..d92b63630
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/Version.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Cloudsmith 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:
+ * Cloudsmith Inc - initial API and implementation.
+ *******************************************************************************/
+
+package org.eclipse.equinox.p2.metadata;
+
+import java.io.Serializable;
+import org.eclipse.equinox.internal.p2.metadata.*;
+
+/**
+ * A class that represents a Version in the Omni Version format. A Version can be though of as an
+ * array of comparable elements and an optional pad value. The pad value is used when comparing
+ * two versions with a different number of segments.
+ *
+ * The Omni Version can convert almost any version into a raw format that it uses for comparisons.
+ * This enables a unified order of all such versions and solves problems that arise when the
+ * version semantics are different. A good example is the OSGi version versus the version used in Maven.
+ * The lack of qualifier in the OSGi version implies that the qualifier is an empty string. So a version
+ * without a qualifier is the smallest of all other versions with the same major,minor,micro number.
+ * With Maven semantics, it's the opposite. If the qualifier is removed, the resulting version is
+ * considered higher then all other versions with the same major, minor, and micro number. The
+ * Omni version solves this by using different raw representations of the OSGi and Maven versions.
+ *
+ * The Omni version addresses a lot of other issues as well, such as reordering of the elements
+ * or treating some parts of a version as irrelevant when comparing.
+ *
+ * The class is signature compatible with {@link org.osgi.framework.Version} but attempts
+ * to use it as such might render a {@link UnsupportedOperationException} in case the
+ * raw vector holds incompatible values. The method {@link #isOSGiCompatible()} can be used
+ * to test.
+ * @since 2.0
+ */
+public abstract class Version implements Comparable<Version>, Serializable {
+ public static final String RAW_PREFIX = "raw:"; //$NON-NLS-1$
+
+ /**
+ * The version that is semantically greater then all other versions.
+ */
+ public static final Version MAX_VERSION = OmniVersion.createMaxVersion();
+
+ /**
+ * The version that is semantically less then all other versions.
+ */
+ public static final Version emptyVersion = OmniVersion.createMinVersion();
+
+ private static final long serialVersionUID = 6218979149720923857L;
+
+ /**
+ * Compile a version format string into a compiled format..
+ *
+ * @param format The format to compile.
+ * @return The compiled format
+ * @throws VersionFormatException If the format could not be compiled
+ */
+ public static IVersionFormat compile(String format) throws VersionFormatException {
+ return VersionFormat.compile(format, 0, format.length());
+ }
+
+ /**
+ * Parses a version identifier from the specified string.
+ *
+ * @param version String representation of the version identifier. Leading
+ * and trailing whitespace will be ignored.
+ * @return A <code>Version</code> object representing the version identifier
+ * or <code>null</code> if <code>version</code> is <code>null</code> or
+ * an empty string.
+ * @throws IllegalArgumentException If <code>version</code> is improperly
+ * formatted.
+ */
+ public static Version create(String version) {
+ return version == null ? null : VersionParser.parse(version, 0, version.length());
+ }
+
+ /**
+ * Creates an OSGi version identifier from the specified numerical components.
+ *
+ * <p>
+ * The qualifier is set to the empty string.
+ *
+ * @param major Major component of the version identifier.
+ * @param minor Minor component of the version identifier.
+ * @param micro Micro component of the version identifier.
+ * @throws IllegalArgumentException If the numerical components are
+ * negative.
+ */
+ public static Version createOSGi(int major, int minor, int micro) {
+ return createOSGi(major, minor, micro, null);
+ }
+
+ /**
+ * Creates an OSGi version identifier from the specified components.
+ *
+ * @param major Major component of the version identifier.
+ * @param minor Minor component of the version identifier.
+ * @param micro Micro component of the version identifier.
+ * @param qualifier Qualifier component of the version identifier. If
+ * <code>null</code> is specified, then the qualifier will be set to
+ * the empty string.
+ * @throws IllegalArgumentException If the numerical components are negative
+ * or the qualifier string is invalid.
+ */
+ public static Version createOSGi(int major, int minor, int micro, String qualifier) {
+ Comparable<?> logicQualifier;
+ if (qualifier == null || qualifier.length() == 0) {
+ if (major == 0 && minor == 0 && micro == 0)
+ return emptyVersion;
+ logicQualifier = VersionVector.MINS_VALUE; // So that we can do identity compare
+ } else if (qualifier.equals(IVersionFormat.DEFAULT_MAX_STRING_TRANSLATION))
+ logicQualifier = VersionVector.MAXS_VALUE;
+ else
+ logicQualifier = qualifier;
+ return new OSGiVersion(major, minor, micro, logicQualifier);
+ }
+
+ /**
+ * Create an omni version from an OSGi <code>version</code>.
+ * @param version The OSGi version. Can be <code>null</code>.
+ * @return The created omni version
+ */
+ public static Version fromOSGiVersion(org.osgi.framework.Version version) {
+ if (version == null)
+ return null;
+ if (version.getMajor() == Integer.MAX_VALUE && version.getMicro() == Integer.MAX_VALUE && version.getMicro() == Integer.MAX_VALUE)
+ return MAX_VERSION;
+ return createOSGi(version.getMajor(), version.getMinor(), version.getMicro(), version.getQualifier());
+ }
+
+ /**
+ * Parses a version identifier from the specified string. This method is for backward
+ * compatibility with OSGi and will return the OSGi &quot;0.0.0&quot; version when
+ * the provided string is empty or <code>null</code>.
+ *
+ * @param version String representation of the version identifier. Leading
+ * and trailing whitespace will be ignored.
+ * @return A <code>Version</code> object representing the version
+ * identifier. If <code>version</code> is <code>null</code> or
+ * the empty string then the OSGi <code>emptyVersion</code> will be
+ * returned.
+ * @throws IllegalArgumentException If <code>version</code> is improperly
+ * formatted.
+ * @see #create(String)
+ */
+ public static Version parseVersion(String version) {
+ if (version == null || version.length() == 0)
+ return Version.emptyVersion;
+ Version v = create(version);
+ return v == null ? Version.emptyVersion : v;
+ }
+
+ /**
+ * Convert <code>version</code> into its OSGi equivalent if possible.
+ *
+ * @param version The version to convert. Can be <code>null</code>
+ * @return The converted version or <code>null</code> if the argument was <code>null</code>
+ * @throws UnsupportedOperationException if the version could not be converted into an OSGi version
+ */
+ public static org.osgi.framework.Version toOSGiVersion(Version version) {
+ if (version == null)
+ return null;
+ if (version == emptyVersion)
+ return org.osgi.framework.Version.emptyVersion;
+ if (version == MAX_VERSION)
+ return new org.osgi.framework.Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+
+ BasicVersion bv = (BasicVersion) version;
+ return new org.osgi.framework.Version(bv.getMajor(), bv.getMinor(), bv.getMicro(), bv.getQualifier());
+ }
+
+ /**
+ * Returns the optional format.
+ */
+ public abstract IVersionFormat getFormat();
+
+ /**
+ * Returns the <code>original</code> part of the string for this version
+ * or <code>null</code> if no such part was provided when the version was
+ * created. An OSGi type version will always return the OSGi string representation.
+ *
+ * @return The <code>original</code> part of the version string or
+ * <code>null</code> if that part was missing.
+ */
+ public abstract String getOriginal();
+
+ /**
+ * Returns the pad value used when comparing this versions to
+ * versions that has a larger number of segments
+ * @return The pad value or <code>null</code> if not set.
+ */
+ public abstract Comparable<?> getPad();
+
+ /**
+ * An element from the raw vector representation of this version.
+ * @param index The zero based index of the desired element
+ * @return An element from the raw vector
+ */
+ public abstract Comparable<?> getSegment(int index);
+
+ /**
+ * Returns the number of elements in the raw vector representation of this version.
+ * @return The number of elements in the raw vector.
+ */
+ public abstract int getSegmentCount();
+
+ /**
+ * Checks if this version is in compliance with the OSGi version spec.
+ * @return A flag indicating whether the version is OSGi compatible or not.
+ */
+ public abstract boolean isOSGiCompatible();
+
+ public String toString() {
+ StringBuffer buf = new StringBuffer(20);
+ toString(buf);
+ return buf.toString();
+ }
+
+ /**
+ * Appends the string representation of this version onto the
+ * <code>sb</code> StringBuffer.
+ * @param sb The buffer that will receive the version string
+ */
+ public abstract void toString(StringBuffer sb);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/FormatException.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/VersionFormatException.java
index 3e9855f54..4aeb4c9e1 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/FormatException.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/VersionFormatException.java
@@ -8,17 +8,17 @@
* Contributors:
* Cloudsmith Inc. - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
+package org.eclipse.equinox.p2.metadata;
/**
- * Exception thrown by the {@link VersionFormatParser}
- *
+ * Exception thrown when parsing Omni Version formats.
+ * @since 2.0
*/
-public class FormatException extends Exception {
+public class VersionFormatException extends Exception {
private static final long serialVersionUID = -867104101610941043L;
- public FormatException(String message) {
+ public VersionFormatException(String message) {
super(message);
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionRange.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/VersionRange.java
index 16484abe6..6d93d42a7 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/VersionRange.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/VersionRange.java
@@ -9,11 +9,10 @@
* IBM Corporation - initial API and implementation
* Cloudsmith Inc - rewrite to handle non-OSGi versions.
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata;
-
-import org.eclipse.equinox.internal.p2.metadata.Messages;
+package org.eclipse.equinox.p2.metadata;
import java.io.Serializable;
+import org.eclipse.equinox.internal.p2.metadata.*;
import org.eclipse.osgi.util.NLS;
/**
@@ -22,13 +21,11 @@ import org.eclipse.osgi.util.NLS;
*
* @Immutable
* @noextend This class is not intended to be subclassed by clients.
+ * @since 2.0
*/
public class VersionRange implements Serializable {
private static final long serialVersionUID = 4988030307298088028L;
- static final Version OSGi_versionMin = new Version(0, 0, 0);
- static final Version OSGi_versionMax = new Version(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
-
/**
* TODO: This should not be OSGi but it has to be that for now since the resolver creates
* a filter where the min and max are converted into strings. When the filter is evaluated an
@@ -36,7 +33,7 @@ public class VersionRange implements Serializable {
*
* An empty OSGi Version range.
*/
- public static final VersionRange emptyRange = new VersionRange(OSGi_versionMin, true, OSGi_versionMax, true);
+ public static final VersionRange emptyRange = new VersionRange(Version.emptyVersion, true, Version.MAX_VERSION, true);
private final Version minVersion;
private final boolean includeMin;
@@ -75,26 +72,19 @@ public class VersionRange implements Serializable {
public VersionRange(Version minVersion, boolean includeMin, Version maxVersion, boolean includeMax) {
if (minVersion == null) {
if (maxVersion == null) {
- // For backward compatibility with the OSGi version version range
- minVersion = OSGi_versionMin;
- maxVersion = OSGi_versionMax;
+ minVersion = Version.emptyVersion;
+ maxVersion = Version.MAX_VERSION;
} else
- minVersion = maxVersion.getFormat() == VersionFormat.OSGI_FORMAT ? OSGi_versionMin : Version.MIN_VERSION;
+ minVersion = Version.emptyVersion;
} else {
if (maxVersion == null)
- maxVersion = minVersion.getFormat() == VersionFormat.OSGI_FORMAT ? OSGi_versionMax : Version.MAX_VERSION;
+ maxVersion = Version.MAX_VERSION;
else {
if (minVersion != maxVersion && minVersion.equals(maxVersion))
maxVersion = minVersion;
else if (!(minVersion.getFormat() == null ? maxVersion.getFormat() == null : minVersion.getFormat().equals(maxVersion.getFormat()))) {
- // We always allow the MIN and MAX boundaries but if the other end is OSGi, then they too must be OSGi
- if (minVersion.equals(Version.MIN_VERSION)) {
- if (maxVersion.getFormat() == VersionFormat.OSGI_FORMAT)
- minVersion = OSGi_versionMin;
- } else if (maxVersion.equals(Version.MAX_VERSION)) {
- if (minVersion.getFormat() == VersionFormat.OSGI_FORMAT)
- maxVersion = OSGi_versionMax;
- } else
+ // We always allow the MIN and MAX boundaries
+ if (!(minVersion.equals(Version.emptyVersion) || maxVersion.equals(Version.MAX_VERSION)))
throw new IllegalArgumentException(NLS.bind(Messages.range_boundaries_0_and_1_cannot_have_different_formats, minVersion, maxVersion));
}
}
@@ -121,9 +111,9 @@ public class VersionRange implements Serializable {
}
if (pos >= top) {
- minVersion = OSGi_versionMin;
+ minVersion = Version.emptyVersion;
includeMin = true;
- maxVersion = OSGi_versionMax;
+ maxVersion = Version.MAX_VERSION;
includeMax = true;
return;
}
@@ -131,7 +121,7 @@ public class VersionRange implements Serializable {
char c = versionRange.charAt(pos);
int[] position = new int[1];
boolean rawPrefix = false;
- VersionFormat fmt = null;
+ IVersionFormat fmt = null;
if (VersionParser.isLetter(c)) {
if (versionRange.startsWith("raw:", pos)) { //$NON-NLS-1$
rawPrefix = true;
@@ -238,28 +228,14 @@ public class VersionRange implements Serializable {
}
}
}
- Version minV = VersionFormat.parseRaw(minStr, fmt, origMin);
-
- // We might have parsed the Version.MIN_VERSION. If so, replace it. The format is incorrect.
- //
- boolean isOSGi = (fmt == VersionFormat.OSGI_FORMAT);
- boolean isMinMin = (minV.getVector().length == 0 && minV.getPad() == null);
- minVersion = isMinMin ? (isOSGi ? OSGi_versionMin : Version.MIN_VERSION) : minV;
-
+ minVersion = VersionFormat.parseRaw(minStr, fmt, origMin);
if (maxStr != null) {
if (maxStr.equals(minStr))
- maxVersion = minV;
- else {
- Version maxV = VersionFormat.parseRaw(maxStr, fmt, origMax);
- Comparable[] maxVector = maxV.getVector();
-
- // We might have parsed the Version.MAX_VERSION. If so, replace it. The format is incorrect.
- //
- boolean isMaxMax = (maxVector.length == 0 && maxV.getPad() == VersionVector.MAX_VALUE);
- maxVersion = isMaxMax ? (isOSGi ? OSGi_versionMax : Version.MAX_VERSION) : maxV;
- }
+ maxVersion = minVersion;
+ else
+ maxVersion = VersionFormat.parseRaw(maxStr, fmt, origMax);
} else
- maxVersion = (fmt == VersionFormat.OSGI_FORMAT ? OSGi_versionMax : Version.MAX_VERSION);
+ maxVersion = Version.MAX_VERSION;
} else {
if (fmt == null)
fmt = VersionFormat.OSGI_FORMAT;
@@ -270,13 +246,13 @@ public class VersionRange implements Serializable {
else
maxVersion = fmt.parse(maxStr);
} else {
- maxVersion = (fmt == VersionFormat.OSGI_FORMAT) ? OSGi_versionMax : Version.MAX_VERSION;
+ maxVersion = Version.MAX_VERSION;
}
}
validateRange();
}
- private static VersionFormat parseFormat(String versionRange, int[] position) {
+ private static IVersionFormat parseFormat(String versionRange, int[] position) {
int pos = VersionParser.skipWhite(versionRange, position[0]);
if (!versionRange.startsWith("format(", pos)) //$NON-NLS-1$
return null;
@@ -286,7 +262,7 @@ public class VersionRange implements Serializable {
try {
position[0] = end + 1;
return VersionFormat.compile(versionRange, pos, end);
- } catch (FormatException e) {
+ } catch (VersionFormatException e) {
throw new IllegalArgumentException(e.getMessage());
}
}
@@ -294,8 +270,8 @@ public class VersionRange implements Serializable {
/**
* Returns the version format.
*/
- public VersionFormat getFormat() {
- return minVersion.equals(Version.MIN_VERSION) ? maxVersion.getFormat() : minVersion.getFormat();
+ public IVersionFormat getFormat() {
+ return minVersion.equals(Version.emptyVersion) ? maxVersion.getFormat() : minVersion.getFormat();
}
/**
@@ -426,9 +402,15 @@ public class VersionRange implements Serializable {
}
public void toString(StringBuffer result) {
- VersionFormat fmt = getFormat();
+ boolean gtEqual = includeMin && includeMax && Version.MAX_VERSION.equals(maxVersion);
+ if (gtEqual && Version.emptyVersion.equals(minVersion)) {
+ minVersion.toString(result);
+ return;
+ }
+
+ IVersionFormat fmt = getFormat();
if (fmt == VersionFormat.OSGI_FORMAT) {
- if (includeMin && includeMax && OSGi_versionMax.equals(maxVersion)) {
+ if (gtEqual) {
minVersion.toString(result);
} else {
result.append(includeMin ? '[' : '(');
@@ -440,15 +422,14 @@ public class VersionRange implements Serializable {
return;
}
- boolean gtEqual = includeMin && includeMax && Version.MAX_VERSION.equals(maxVersion);
result.append("raw:"); //$NON-NLS-1$
if (gtEqual) {
- minVersion.rawToString(result, true);
+ ((BasicVersion) minVersion).rawToString(result, true);
} else {
result.append(includeMin ? '[' : '(');
- minVersion.rawToString(result, true);
+ ((BasicVersion) minVersion).rawToString(result, true);
result.append(',');
- maxVersion.rawToString(result, true);
+ ((BasicVersion) maxVersion).rawToString(result, true);
result.append(includeMax ? ']' : ')');
}
boolean hasOriginal = (minVersion.getOriginal() != null || maxVersion.getOriginal() != null);
@@ -459,14 +440,14 @@ public class VersionRange implements Serializable {
if (hasOriginal) {
result.append(':');
if (gtEqual) {
- minVersion.originalToString(result, true);
+ ((BasicVersion) minVersion).originalToString(result, true);
} else {
- if (Version.MIN_VERSION.equals(minVersion))
- minVersion.rawToString(result, true);
+ if (Version.emptyVersion.equals(minVersion))
+ ((BasicVersion) minVersion).rawToString(result, true);
else
- minVersion.originalToString(result, true);
+ ((BasicVersion) minVersion).originalToString(result, true);
result.append(',');
- maxVersion.originalToString(result, true);
+ ((BasicVersion) maxVersion).originalToString(result, true);
}
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionParseException.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionParseException.java
new file mode 100644
index 000000000..059f30482
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionParseException.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+/**
+ * An exception used by an expression parser that indicates that something went wrong when
+ * parsing.
+ * @since 2.0
+ */
+public class ExpressionParseException extends RuntimeException {
+ private static final long serialVersionUID = 8432875384760577764L;
+
+ public ExpressionParseException(String message) {
+ super(message);
+ }
+
+ public ExpressionParseException(String expression, String message, int position) {
+ super("Parse error in string \"" + expression + "\": " + message + " at position " + position); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionUtil.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionUtil.java
new file mode 100644
index 000000000..02861aeed
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/ExpressionUtil.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+import org.eclipse.equinox.internal.p2.metadata.expression.*;
+import org.eclipse.equinox.internal.p2.metadata.expression.parser.ExpressionParser;
+import org.eclipse.equinox.internal.p2.metadata.expression.parser.LDAPFilterParser;
+
+/**
+ * Global access to factory, parser, and methods for introspection
+ */
+public abstract class ExpressionUtil {
+ private static final LDAPFilterParser ldapFilterParser = new LDAPFilterParser(ExpressionFactory.INSTANCE);
+ private static final ExpressionParser expressionParser = new ExpressionParser(ExpressionFactory.INSTANCE);
+
+ /**
+ * Returns the global expression factory
+ * @return The global expression factory.
+ */
+ public static IExpressionFactory getFactory() {
+ return ExpressionFactory.INSTANCE;
+ }
+
+ /**
+ * Creates and returns a new expression parser
+ * @return The new parser
+ */
+ public static IExpressionParser newParser() {
+ return new ExpressionParser(getFactory());
+ }
+
+ /**
+ * Parse an LDAP filter from the <code>filter</code> string. If <code>filter</code> is <code>null</code>
+ * or a string that is empty or only consists of whitespace, then this method returns <code>null</code>.
+ * @param filter The filter to parse. Can be <code>null</code> or empty.
+ * @return An expression that corresponds to the LDAP filter or <code>null</code>.
+ * @throws ExpressionParseException If the syntax was invalid
+ */
+ public static IFilterExpression parseLDAP(String filter) throws IllegalArgumentException {
+ filter = trimmedOrNull(filter);
+ return filter == null ? null : ldapFilterParser.parse(filter);
+ }
+
+ /**
+ * Parse a boolean Expression from the <code>expression</code> string. If <code>expression</code> is <code>null</code>
+ * or a string that is empty or only consists of whitespace, then this method returns <code>null</code>.
+ * @param expression The expression to parse. Can be <code>null</code> or empty.
+ * @return An expression that corresponds to the LDAP filter or <code>null</code>.
+ * @throws ExpressionParseException If the syntax was invalid
+ */
+ public static IExpression parse(String expression) {
+ expression = trimmedOrNull(expression);
+ return expression == null ? null : expressionParser.parse(expression);
+ }
+
+ /**
+ * If <code>str</code> is <code>null</code>, then this method returns <code>null</code>.
+ * Otherwise <code>str</code> is trimmed from whitespace at both ends. If the result
+ * of the trim is an empty string, then <code>null</code> is returned, otherwise the
+ * result of the trim is returned.
+ * @param str The string to trim. Can be <code>null</code>.
+ * @return The trimmed string or <code>null</code>.
+ */
+ public static String trimmedOrNull(String str) {
+ if (str != null) {
+ str = str.trim();
+ if (str.length() == 0)
+ str = null;
+ }
+ return str;
+ }
+
+ /**
+ * Obtains the Left Hand Side (LHS) of a binary expression.
+ * @param expression The expression to introspect
+ * @return The left hand side operator
+ * @throws IllegalArgumentException if the expression is not a binary expression
+ * @see IExpression#TYPE_AT
+ * @see IExpression#TYPE_EQUALS
+ * @see IExpression#TYPE_GREATER
+ * @see IExpression#TYPE_GREATER_EQUAL
+ * @see IExpression#TYPE_LESS
+ * @see IExpression#TYPE_LESS_EQUAL
+ * @see IExpression#TYPE_MATCHES
+ * @see IExpression#TYPE_NOT_EQUALS
+ */
+ public static IExpression getLHS(IExpression expression) {
+ if (expression instanceof Binary)
+ return ((Binary) expression).lhs;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Obtains the name of a variable or member expression.
+ * @param expression The expression to introspect
+ * @return The name of the expression
+ * @throws IllegalArgumentException if the expression is not a variable or a member
+ * @see IExpression#TYPE_MEMBER
+ * @see IExpression#TYPE_VARIABLE
+ */
+ public static String getName(IExpression expression) {
+ if (expression instanceof Member)
+ return ((Member) expression).getName();
+ if (expression instanceof Variable)
+ return ((Variable) expression).getName();
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Obtains the operand of an unary expression
+ * @param expression The expression to introspect
+ * @return The expression operand
+ * @throws IllegalArgumentException if the expression is not an unary expression
+ * @see IExpression#TYPE_ALL
+ * @see IExpression#TYPE_EXISTS
+ * @see IExpression#TYPE_LAMBDA
+ * @see IExpression#TYPE_NOT
+ */
+ public static IExpression getOperand(IExpression expression) {
+ if (expression instanceof Unary)
+ return ((Unary) expression).operand;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Obtains the operands of an n-ary expression
+ * @param expression The expression to introspect
+ * @return The expression operand
+ * @throws IllegalArgumentException if the expression is not a n-ary expression
+ * @see IExpression#TYPE_AND
+ * @see IExpression#TYPE_OR
+ */
+ public static IExpression[] getOperands(IExpression expression) {
+ if (expression instanceof NAry)
+ return ((NAry) expression).operands;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Obtains the Right Hand Side (RHS) of a binary expression.
+ * @param expression The expression to introspect
+ * @return The right hand side operator
+ * @throws IllegalArgumentException if the expression is not a binary expression
+ * @see IExpression#TYPE_AT
+ * @see IExpression#TYPE_EQUALS
+ * @see IExpression#TYPE_GREATER
+ * @see IExpression#TYPE_GREATER_EQUAL
+ * @see IExpression#TYPE_LESS
+ * @see IExpression#TYPE_LESS_EQUAL
+ * @see IExpression#TYPE_MATCHES
+ * @see IExpression#TYPE_NOT_EQUALS
+ */
+ public static IExpression getRHS(IExpression expression) {
+ if (expression instanceof Binary)
+ return ((Binary) expression).rhs;
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Obtains the value of a literal expression
+ * @param expression The expression to introspect
+ * @return The literal value
+ * @throws IllegalArgumentException if the expression is not a literal
+ * @see IExpression#TYPE_LITERAL
+ */
+ public static Object getValue(IExpression expression) {
+ if (expression instanceof Literal)
+ return ((Literal) expression).value;
+ throw new IllegalArgumentException();
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IEvaluationContext.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IEvaluationContext.java
new file mode 100644
index 000000000..4b049c054
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IEvaluationContext.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+/**
+ * The evaluation context. Contexts can be nested and new contexts are pushed for each closure
+ * during an evaluation of an expression.
+ * @since 2.0
+ */
+public interface IEvaluationContext {
+ /**
+ * Retrieve the value of the given <code>variable</code> from this context
+ * @param variable The variable who's value should be retrieved
+ * @return The current value for the variable
+ */
+ Object getValue(IExpression variable);
+
+ /**
+ * Set the current value for the given <code>variable</code> to <code>value</code>
+ * @param variable The variable who's value should be set
+ * @param value The new value for the variable.
+ */
+ void setValue(IExpression variable, Object value);
+
+ /**
+ * Returns the value of the parameter at the given <code>position</code>
+ * @param position The zero based position for the parameter
+ * @return The parameter value
+ */
+ Object getParameter(int position);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpression.java
new file mode 100644
index 000000000..889675381
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpression.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+/**
+ * A node in the expression tree
+ */
+public interface IExpression {
+ int TYPE_ALL = 1;
+ int TYPE_AND = 2;
+ int TYPE_AT = 3;
+ int TYPE_EQUALS = 4;
+ int TYPE_EXISTS = 5;
+ int TYPE_GREATER = 6;
+ int TYPE_GREATER_EQUAL = 7;
+ int TYPE_LAMBDA = 8;
+ int TYPE_LESS = 9;
+ int TYPE_LESS_EQUAL = 10;
+ int TYPE_LITERAL = 11;
+ int TYPE_MATCHES = 12;
+ int TYPE_MEMBER = 13;
+ int TYPE_NOT = 14;
+ int TYPE_NOT_EQUALS = 15;
+ int TYPE_OR = 16;
+ int TYPE_PARAMETER = 17;
+ int TYPE_VARIABLE = 18;
+
+ /**
+ * Let the visitor visit this instance and all expressions that this
+ * instance contains.
+ * @param visitor The visiting visitor.
+ * @return <code>true</code> if the visitor should continue visiting, <code>false</code> otherwise.
+ */
+ boolean accept(IExpressionVisitor visitor);
+
+ /**
+ * Evaluate this expression with given context and variables.
+ * @param context The evaluation context
+ * @return The result of the evaluation.
+ */
+ Object evaluate(IEvaluationContext context);
+
+ /**
+ * Returns the expression type (see TYPE_xxx constants).
+ */
+ int getExpressionType();
+
+ /**
+ * Appends the string representation of this expression to the collector <code>collector</code>.
+ */
+ void toString(StringBuffer collector);
+
+ /**
+ * Appends the an LDAP filter representation of this expression to the <code>collector</code>.
+ * @throws UnsupportedOperationException if the expression contains nodes
+ * that cannot be represented in an LDAP filter
+ */
+ void toLDAPString(StringBuffer collector);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionFactory.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionFactory.java
new file mode 100644
index 000000000..f76079a5d
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionFactory.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+import java.util.List;
+
+/**
+ * This inteface provides all the factory methods needed to create the
+ * nodes of the expression tree.
+ */
+public interface IExpressionFactory {
+ String FUNC_BOOLEAN = "boolean"; //$NON-NLS-1$
+ String FUNC_VERSION = "version"; //$NON-NLS-1$
+ String FUNC_CLASS = "class"; //$NON-NLS-1$
+ String FUNC_RANGE = "range"; //$NON-NLS-1$
+ String FUNC_FILTER = "filter"; //$NON-NLS-1$
+
+ IExpression[] NO_ARGS = new IExpression[0];
+
+ /**
+ * Create a collection filter that yields true if the <code>lambda</code> yields true for
+ * all of the elements of the <code>collection</code>
+ * @param collection The collection providing the elements to test
+ * @param lambda The lambda that performs the test
+ * @return A boolean expression
+ */
+ IExpression all(IExpression collection, IExpression lambda);
+
+ /**
+ * Create a logical <i>and</i> of its <code>operands</code>.
+ * @param operands The boolean operands
+ * @return A boolean expression
+ */
+ IExpression and(IExpression... operands);
+
+ /**
+ * Create an lookup of <code>key</code> in the <code>target</code>.
+ * The key expression should evaluate to a string or an integer.
+ * @param target The target for the lookup
+ * @param key The key to use for the lookup
+ * @return A lookup expression
+ */
+ IExpression at(IExpression target, IExpression key);
+
+ /**
+ * Create an evaluation context with one single variable
+ * @param params Indexed parameters to use in the expression
+ * @return the context
+ */
+ IEvaluationContext createContext(Object... params);
+
+ /**
+ * Create an evaluation context with one single variable
+ * @param params Indexed parameters to use in the expression
+ * @param variables The variables that will be maintained by the context
+ * @return the context
+ */
+ IEvaluationContext createContext(IExpression[] variables, Object... params);
+
+ /**
+ * Creates an expression that evaluates to the constant <code>value</code>.
+ * @param value The constant
+ * @return A constant expression
+ */
+ IExpression constant(Object value);
+
+ /**
+ * Create an expression that tests if <code>lhs</code> is equal to <code>rhs</code>.
+ * @param lhs The left hand side value.
+ * @param rhs The right hand side value.
+ * @return A boolean expression
+ */
+ IExpression equals(IExpression lhs, IExpression rhs);
+
+ /**
+ * Create a collection filter that yields true if the <code>lambda</code> yields true for
+ * at least one of the elements of the <code>collection</code>
+ * @param collection The collection providing the elements to test
+ * @param lambda The lambda that performs the test
+ * @return A boolean expression
+ */
+ IExpression exists(IExpression collection, IExpression lambda);
+
+ /**
+ * Creates a top level expression suitable for predicate matching
+ * @param expression The boolean expression
+ * @return A top level predicate expression
+ */
+ IFilterExpression filterExpression(IExpression expression);
+
+ /**
+ * Create an expression that tests if <code>lhs</code> is greater than <code>rhs</code>.
+ * @param lhs The left hand side value.
+ * @param rhs The right hand side value.
+ * @return A boolean expression
+ */
+ IExpression greater(IExpression lhs, IExpression rhs);
+
+ /**
+ * Create an expression that tests if <code>lhs</code> is greater than or equal to <code>rhs</code>.
+ * @param lhs The left hand side value.
+ * @param rhs The right hand side value.
+ * @return A boolean expression
+ */
+ IExpression greaterEqual(IExpression lhs, IExpression rhs);
+
+ /**
+ * Creates an indexed parameter expression
+ * @param index The index to use
+ * @return a parameter expression
+ */
+ IExpression indexedParameter(int index);
+
+ /**
+ * Creates a lambda expression that takes exactly one variable. Suitable for use
+ * in most collection expressions.
+ * @param variable The element variable that the lambda uses
+ * @param body The body of the lambda
+ * @return A lambda expression
+ */
+ IExpression lambda(IExpression variable, IExpression body);
+
+ /**
+ * Create an expression that tests if <code>lhs</code> is less than <code>rhs</code>.
+ * @param lhs The left hand side value.
+ * @param rhs The right hand side value.
+ * @return A boolean expression
+ */
+ IExpression less(IExpression lhs, IExpression rhs);
+
+ /**
+ * Create an expression that tests if <code>lhs</code> is less than or equal to <code>rhs</code>.
+ * @param lhs The left hand side value.
+ * @param rhs The right hand side value.
+ * @return A boolean expression
+ */
+ IExpression lessEqual(IExpression lhs, IExpression rhs);
+
+ /**
+ * Performs boolean normalizations on the expression to create a canonical form.
+ * @param operands The operands to normalize
+ * @param expressionType The type (must be either {@link IExpression#TYPE_AND}
+ * or {@link IExpression#TYPE_OR}.
+ * @return The normalized expression
+ */
+ IExpression normalize(List<? extends IExpression> operands, int expressionType);
+
+ /**
+ * Create an expression that tests if <code>lhs</code> matches <code>rhs</code>.
+ * @param lhs The left hand side value.
+ * @param rhs The right hand side value.
+ * @return A boolean expression
+ */
+ IExpression matches(IExpression lhs, IExpression rhs);
+
+ /**
+ * Creates a parameterized top level expression suitable for predicate matching
+ * @param expression The boolean expression
+ * @param parameters The parameters to use in the call
+ * @return A top level predicate expression
+ */
+ <T> IMatchExpression<T> matchExpression(IExpression expression, Object... parameters);
+
+ /**
+ * Creates a member accessor expression.
+ * @param target The target for the member access
+ * @param name The name of the member
+ * @return A member expression
+ */
+ IExpression member(IExpression target, String name);
+
+ /**
+ * Creates an expression that negates the result of evaluating its <code>operand</code>.
+ * @param operand The boolean expression to negate
+ * @return A boolean expression
+ */
+ IExpression not(IExpression operand);
+
+ /**
+ * Create a logical <i>or</i> of its <code>operands</code>.
+ * @param operands The boolean operands
+ * @return A boolean expression
+ */
+ IExpression or(IExpression... operands);
+
+ /**
+ * Returns the variable that represents <code>this</this> in an expression
+ * @return The <code>this</this> variable.
+ */
+ IExpression thisVariable();
+
+ /**
+ * Creates an expression that represents a variable
+ * @param name The name of the variable
+ * @return A variable expression
+ */
+ IExpression variable(String name);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionParser.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionParser.java
new file mode 100644
index 000000000..6e716af06
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionParser.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+/**
+ * A parser that produces an expression tree based on a string representation. An
+ * implementation will use the {@link IExpressionFactory} to create the actual expressions
+ * @since 2.0
+ */
+public interface IExpressionParser {
+ /**
+ * Create a new expression. The expression will have access to the global
+ * variable and to the context parameters.
+ * @param exprString The string representing the boolean expression.
+ * @return The resulting expression tree.
+ */
+ IExpression parse(String exprString);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionVisitor.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionVisitor.java
new file mode 100644
index 000000000..1c314d804
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IExpressionVisitor.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+/**
+ * A general purpose visitor that will visit each node in an expression tree.
+ * @since 2.0
+ */
+public interface IExpressionVisitor {
+ /**
+ * The method that will be called for each expression that is
+ * visited.
+ * @param expression The expression that the visitor visits.
+ * @return <code>true</code> to continue visiting other expressions or
+ * <code>false</code> to break out.
+ */
+ boolean visit(IExpression expression);
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IFilterExpression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IFilterExpression.java
new file mode 100644
index 000000000..8e5bcba92
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IFilterExpression.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+import java.util.Map;
+import org.osgi.framework.Filter;
+
+/**
+ * An interface that combines the IExpression with the LDAP filter. The
+ * string representation is the LDAP filter syntax.
+ * @since 2.0
+ */
+public interface IFilterExpression extends IExpression, Filter {
+ /**
+ * Filter using a <code>Map</code>. This <code>Filter</code> is
+ * executed using the specified <code>Map</code>'s keys and values.
+ * The keys are case insensitively matched with this <code>Filter</code>.
+ *
+ * @param map The <code>Map</code> whose keys are used in the
+ * match.
+ * @return <code>true</code> if the <code>map</code>'s keys and
+ * values match this filter; <code>false</code> otherwise.
+ * @throws IllegalArgumentException If <code>map</code> contains case
+ * variants of the same key name.
+ */
+ boolean match(Map<String, ? extends Object> map);
+
+ /**
+ * Filter with case sensitivity using a <code>Map</code>. This
+ * <code>Filter</code> is executed using the specified
+ * <code>Map</code>'s keys and values. The keys are case sensitively
+ * matched with this <code>Filter</code>.
+ *
+ * @param map The <code>Map</code> whose keys are used in the
+ * match.
+ * @return <code>true</code> if the <code>map</code>'s keys and
+ * values match this filter; <code>false</code> otherwise.
+ */
+ boolean matchCase(Map<String, ? extends Object> map);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IMatchExpression.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IMatchExpression.java
new file mode 100644
index 000000000..419e3f4ba
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/IMatchExpression.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+/**
+ * A match expression is a boolean expression matching a candidate of a
+ * specific type. An {@link IEvaluationContext} is needed in order to evaluate
+ * a match and this class provides two ways of doing that. Either a context
+ * is created first and then reused in several subsequent calls to
+ * {@link #isMatch(IEvaluationContext, Object)} or, if no repeated calls are
+ * expected, the {@link #isMatch(Object)} method can be used. It will then
+ * create a context on each call.
+ */
+public interface IMatchExpression<T> extends IExpression {
+ /**
+ * <p>Creates a new context to be passed to repeated subsequent evaluations. The context
+ * will introduce 'this' as an uninitialized variable and make the parameters available.
+ * @return A new evaluation context.
+ */
+ IEvaluationContext createContext();
+
+ /**
+ * Returns the parameters that this match expression was created with.
+ * @return An array of parameters, possibly empty but never <code>null</code>.
+ */
+ Object[] getParameters();
+
+ /**
+ * This method creates a new evaluation context and assigns the <code>candidate</code>
+ * to the 'this' variable of the <code>context</code> and then evaluates the expression.
+ * This is essentially a short form for <pre>isMatch(createContext(), candidate)</pre>.
+ * @param candidate The object to test.
+ * @return the result of the evaluation.
+ */
+ boolean isMatch(T candidate);
+
+ /**
+ * This method assigns <code>candidate</code> to the 'this' variable of the
+ * <code>context</code> and then evaluates the expression.
+ * @param context A context
+ * @param candidate The object to test.
+ * @return the result of the evaluation.
+ */
+ boolean isMatch(IEvaluationContext context, T candidate);
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/SimplePattern.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/SimplePattern.java
new file mode 100644
index 000000000..12087cb94
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/expression/SimplePattern.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2009 - 2010 Cloudsmith Inc. 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:
+ * Cloudsmith Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.expression;
+
+import java.io.Serializable;
+
+/**
+ * A simple compiled pattern. It supports two kinds of wildcards. The '*' (any character zero to many times)
+ * and the '?' (any character exactly one time).
+ * @since 2.0
+ */
+public class SimplePattern implements Serializable, Comparable<SimplePattern> {
+ private static final long serialVersionUID = -2477990705739062410L;
+
+ /**
+ * Matches the <code>value</code> with the compiled expression. The value
+ * is considered matching if all characters are matched by the expression. A
+ * partial match is not enough.
+ * @param value The value to match
+ * @return <code>true</code> if the value was a match.
+ */
+ public synchronized boolean isMatch(CharSequence value) {
+ if (node == null)
+ node = parse(pattern, 0);
+ return node.match(value, 0);
+ }
+
+ public String toString() {
+ return pattern;
+ }
+
+ public int compareTo(SimplePattern o) {
+ return pattern.compareTo(o.pattern);
+ }
+
+ public boolean equals(Object o) {
+ return o == this || (o instanceof SimplePattern && ((SimplePattern) o).pattern.equals(pattern));
+ }
+
+ public int hashCode() {
+ return 3 * pattern.hashCode();
+ }
+
+ private final String pattern;
+ private transient Node node;
+
+ private SimplePattern(String pattern, Node node) {
+ this.pattern = pattern;
+ this.node = node;
+ }
+
+ private static class RubberBandNode extends Node {
+ RubberBandNode(Node next) {
+ super(next);
+ }
+
+ boolean match(CharSequence value, int pos) {
+ if (next == null)
+ return true;
+
+ int top = value.length();
+ while (pos < top) {
+ if (next.match(value, pos++))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static class AnyCharacterNode extends Node {
+ AnyCharacterNode(Node next) {
+ super(next);
+ }
+
+ boolean match(CharSequence value, int pos) {
+ int top = value.length();
+ return next == null ? pos + 1 == top : next.match(value, pos + 1);
+ }
+ }
+
+ private static class ConstantNode extends Node {
+ final String constant;
+
+ ConstantNode(Node next, String constant) {
+ super(next);
+ this.constant = constant;
+ }
+
+ boolean match(CharSequence value, int pos) {
+ int vtop = value.length();
+ int ctop = constant.length();
+ if (ctop + pos > vtop)
+ return false;
+
+ for (int idx = 0; idx < ctop; ++idx, ++pos)
+ if (constant.charAt(idx) != value.charAt(pos))
+ return false;
+
+ return next == null ? true : next.match(value, pos);
+ }
+ }
+
+ private static abstract class Node {
+ final Node next;
+
+ Node(Node next) {
+ this.next = next;
+ }
+
+ abstract boolean match(CharSequence value, int pos);
+ }
+
+ public static SimplePattern compile(String pattern) {
+ if (pattern == null)
+ throw new IllegalArgumentException("Pattern can not be null"); //$NON-NLS-1$
+ return new SimplePattern(pattern, null);
+ }
+
+ private static Node parse(String pattern, int pos) {
+ int top = pattern.length();
+ StringBuffer bld = null;
+ Node parsedNode = null;
+ while (pos < top) {
+ char c = pattern.charAt(pos);
+ switch (c) {
+ case '*' :
+ parsedNode = new RubberBandNode(parse(pattern, pos + 1));
+ break;
+ case '?' :
+ parsedNode = new AnyCharacterNode(parse(pattern, pos + 1));
+ break;
+ case '\\' :
+ if (++pos == top)
+ throw new IllegalArgumentException("Pattern ends with escape"); //$NON-NLS-1$
+ c = pattern.charAt(pos);
+ // fall through
+ default :
+ if (bld == null)
+ bld = new StringBuffer();
+ bld.append(c);
+ ++pos;
+ continue;
+ }
+ break;
+ }
+
+ if (bld != null)
+ parsedNode = new ConstantNode(parsedNode, bld.toString());
+ return parsedNode;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryMemberQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryMemberQuery.java
new file mode 100644
index 000000000..d6324d360
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryMemberQuery.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.query;
+
+import java.util.Collection;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IRequirement;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+/**
+ * A query matching every {@link IInstallableUnit} that is a member
+ * of the specified category.
+ *
+ * @since 2.0
+ */
+public class CategoryMemberQuery extends MatchQuery<IInstallableUnit> {
+ private final Collection<IRequirement> required;
+
+ /**
+ * Creates a new query that will return the members of the
+ * given category. If the specified {@link IInstallableUnit}
+ * is not a category, then no installable unit will satisfy the query.
+ *
+ * @param category The category
+ */
+ public CategoryMemberQuery(IInstallableUnit category) {
+ if (CategoryQuery.isCategory(category))
+ this.required = category.getRequiredCapabilities();
+ else
+ this.required = CollectionUtils.emptyList();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.equinox.internal.provisional.p2.metadata.query.MatchQuery#isMatch(java.lang.Object)
+ */
+ public boolean isMatch(IInstallableUnit candidate) {
+ // since a category lists its members as requirements, then meeting
+ // any requirement means the candidate is a member of the category.
+ for (IRequirement req : required) {
+ if (candidate.satisfies(req))
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryQuery.java
new file mode 100644
index 000000000..d1d86f912
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/CategoryQuery.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.query;
+
+import org.eclipse.equinox.internal.p2.metadata.query.IUPropertyQuery;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * A query matching every {@link IInstallableUnit} that is a category.
+ * @since 2.0
+ */
+public final class CategoryQuery extends MatchQuery<IInstallableUnit> {
+ private static final String PROP_TYPE_CATEGORY = "org.eclipse.equinox.p2.type.category"; //$NON-NLS-1$
+ private IUPropertyQuery query;
+
+ public CategoryQuery() {
+ query = new IUPropertyQuery(PROP_TYPE_CATEGORY, null);
+ }
+
+ public boolean isMatch(IInstallableUnit candidate) {
+ return query.isMatch(candidate);
+ }
+
+ /**
+ * Test if the {@link IInstallableUnit} is a category.
+ * @param iu the element being tested.
+ * @return <tt>true</tt> if the parameter is a category.
+ */
+ public static boolean isCategory(IInstallableUnit iu) {
+ String value = iu.getProperty(PROP_TYPE_CATEGORY);
+ if (value != null && (value.equals(Boolean.TRUE.toString())))
+ return true;
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/ExpressionQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/ExpressionQuery.java
new file mode 100644
index 000000000..ea0cb676b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/ExpressionQuery.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.query;
+
+import org.eclipse.equinox.internal.p2.metadata.expression.ExpressionFactory;
+import org.eclipse.equinox.p2.metadata.expression.*;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * A query that matches candidates against an expression.
+ */
+public class ExpressionQuery<T> extends MatchQuery<T> {
+ private final IMatchExpression<T> expression;
+ private final IEvaluationContext context;
+ private final Class<T> matchingClass;
+
+ public ExpressionQuery(Class<T> matchingClass, IExpression expression, Object... parameters) {
+ this(matchingClass, ExpressionUtil.getFactory().<T> matchExpression(expression, parameters));
+ }
+
+ public ExpressionQuery(Class<T> matchingClass, IMatchExpression<T> expression) {
+ this.matchingClass = matchingClass;
+ this.expression = expression;
+ this.context = expression.createContext();
+ }
+
+ @Override
+ public boolean isMatch(T candidate) {
+ if (!matchingClass.isInstance(candidate))
+ return false;
+ ExpressionFactory.THIS.setValue(context, candidate);
+ return Boolean.TRUE == expression.evaluate(context);
+ }
+
+ public IMatchExpression<T> getExpression() {
+ return expression;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/FragmentQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/FragmentQuery.java
new file mode 100644
index 000000000..fc70dd9e9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/FragmentQuery.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.query;
+
+import org.eclipse.equinox.p2.metadata.IInstallableUnitFragment;
+
+import org.eclipse.equinox.internal.p2.metadata.query.IUPropertyQuery;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * A query matching every {@link IInstallableUnit} that is a category.
+ * @since 2.0
+ */
+public final class FragmentQuery extends MatchQuery<IInstallableUnit> {
+ private static final String PROP_TYPE_FRAGMENT = "org.eclipse.equinox.p2.type.fragment"; //$NON-NLS-1$
+ private IUPropertyQuery query;
+
+ public FragmentQuery() {
+ query = new IUPropertyQuery(PROP_TYPE_FRAGMENT, null);
+ }
+
+ public boolean isMatch(IInstallableUnit candidate) {
+ return query.isMatch(candidate);
+ }
+
+ /**
+ * Test if the {@link IInstallableUnit} is a fragment.
+ * @param iu the element being tested.
+ * @return <tt>true</tt> if the parameter is a fragment.
+ */
+ public static boolean isFragment(IInstallableUnit iu) {
+ if (iu instanceof IInstallableUnitFragment)
+ return true;
+ // String value = iu.getProperty(PROP_TYPE_FRAGMENT);
+ // if (value != null && (value.equals(Boolean.TRUE.toString())))
+ // return true;
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/GroupQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/GroupQuery.java
new file mode 100644
index 000000000..298b04a2f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/GroupQuery.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.query;
+
+import org.eclipse.equinox.internal.p2.metadata.query.IUPropertyQuery;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * A query matching every {@link IInstallableUnit} that is a group.
+ * @since 2.0
+ */
+public final class GroupQuery extends MatchQuery<IInstallableUnit> {
+ private static final String PROP_TYPE_GROUP = "org.eclipse.equinox.p2.type.group"; //$NON-NLS-1$
+ private IUPropertyQuery query;
+
+ public GroupQuery() {
+ query = new IUPropertyQuery(PROP_TYPE_GROUP, null);
+ }
+
+ public boolean isMatch(IInstallableUnit candidate) {
+ return query.isMatch(candidate);
+ }
+
+ /**
+ * Test if the {@link IInstallableUnit} is a group.
+ * @param iu the element being tested.
+ * @return <tt>true</tt> if the parameter is a group.
+ */
+ public static boolean isGroup(IInstallableUnit iu) {
+ String value = iu.getProperty(PROP_TYPE_GROUP);
+ if (value != null && (value.equals(Boolean.TRUE.toString())))
+ return true;
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/InstallableUnitQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/InstallableUnitQuery.java
index a0152a1e5..ac2110b03 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/InstallableUnitQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/InstallableUnitQuery.java
@@ -8,14 +8,20 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.p2.metadata.query;
-import org.eclipse.equinox.internal.provisional.p2.metadata.*;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.metadata.VersionRange;
+
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.IVersionedId;
+import org.eclipse.equinox.p2.query.MatchQuery;
/**
* A query that matches on the id and version of an {@link IInstallableUnit}.
+ * @since 2.0
*/
-public class InstallableUnitQuery extends MatchQuery {
+public class InstallableUnitQuery extends MatchQuery<IInstallableUnit> {
/**
* A convenience query that will match any {@link IInstallableUnit}
* it encounters.
@@ -89,10 +95,7 @@ public class InstallableUnitQuery extends MatchQuery {
/* (non-Javadoc)
* @see org.eclipse.equinox.p2.query2.Query#isMatch(java.lang.Object)
*/
- public boolean isMatch(Object object) {
- if (!(object instanceof IInstallableUnit))
- return false;
- IInstallableUnit candidate = (IInstallableUnit) object;
+ public boolean isMatch(IInstallableUnit candidate) {
if (id != null && !id.equals(candidate.getId()))
return false;
if (range != null && !range.isIncluded(candidate.getVersion()))
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/PatchQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/PatchQuery.java
new file mode 100644
index 000000000..d066db376
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/query/PatchQuery.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation 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:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.equinox.p2.metadata.query;
+
+import org.eclipse.equinox.internal.p2.metadata.query.IUPropertyQuery;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.query.MatchQuery;
+
+/**
+ * A query matching every {@link IInstallableUnit} that is a patch.
+ * @since 2.0
+ */
+public final class PatchQuery extends MatchQuery<IInstallableUnit> {
+ private static final String PROP_TYPE_PATCH = "org.eclipse.equinox.p2.type.patch"; //$NON-NLS-1$
+ private IUPropertyQuery query;
+
+ public PatchQuery() {
+ query = new IUPropertyQuery(PROP_TYPE_PATCH, Boolean.TRUE.toString());
+ }
+
+ public boolean isMatch(IInstallableUnit candidate) {
+ return query.isMatch(candidate);
+ }
+
+ /**
+ * Test if the {@link IInstallableUnit} is a patch.
+ * @param iu the element being tested.
+ * @return <tt>true</tt> if the parameter is a patch.
+ */
+ public static boolean isPatch(IInstallableUnit iu) {
+ String value = iu.getProperty(PROP_TYPE_PATCH);
+ if (value != null && (value.equals(Boolean.TRUE.toString())))
+ return true;
+ return false;
+ }
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/Collector.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/Collector.java
index 024280495..f7e36e07d 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/Collector.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/Collector.java
@@ -9,14 +9,14 @@
* IBM Corporation - initial API and implementation
* EclipseSource - ongoing development
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
-import org.eclipse.equinox.internal.p2.metadata.Messages;
+package org.eclipse.equinox.p2.query;
import java.lang.reflect.Array;
import java.util.*;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
+import org.eclipse.equinox.internal.p2.metadata.Messages;
/**
* A collector is a generic visitor that collects objects passed to it,
@@ -26,9 +26,22 @@ import org.eclipse.core.runtime.NullProgressMonitor;
* <p>
* This default collector just accepts all objects passed to it. Clients may subclass
* to perform different processing on the objects passed to it.
+ * @param <T> The type of object accepted by this collector
+ * @since 2.0
*/
-public class Collector implements IQueryable {
- private Set collected = null;
+public class Collector<T> implements IQueryResult<T> {
+ private Set<T> collected = null;
+
+ public static final Collector<?> EMPTY_COLLECTOR = new Collector<Object>() {
+ public boolean accept(Object val) {
+ return false;
+ }
+ };
+
+ @SuppressWarnings("unchecked")
+ public static final <T> Collector<T> emptyCollector() {
+ return (Collector<T>) EMPTY_COLLECTOR;
+ }
/**
* Creates a new collector.
@@ -49,22 +62,33 @@ public class Collector implements IQueryable {
* @return <code>true</code> if the traversal should continue,
* or <code>false</code> to indicate the traversal should stop.
*/
- public boolean accept(Object object) {
+ public boolean accept(T object) {
getCollection().add(object);
return true;
}
/**
- * Returns the collection that is being used to collect results. Unlike {@linkplain #toCollection()},
+ * Adds the elements from one collector to this collector
+ * @param queryResult The collector from which the elements should be retrieved
+ */
+ public void addAll(IQueryResult<T> queryResult) {
+ boolean keepGoing = true;
+ for (Iterator<T> iter = queryResult.iterator(); iter.hasNext() && keepGoing;) {
+ keepGoing = accept(iter.next());
+ }
+ }
+
+ /**
+ * Returns the collection that is being used to collect results. Unlike {@linkplain #toSet()},
* this returns the actual modifiable collection that is being used to store results. The
* return value is only intended to be used within subclasses and should not be exposed
* outside of a collection class.
*
* @return the collection being used to collect results.
*/
- protected Collection getCollection() {
+ protected Collection<T> getCollection() {
if (collected == null)
- collected = new HashSet();
+ collected = new HashSet<T>();
return collected;
}
@@ -82,8 +106,8 @@ public class Collector implements IQueryable {
*
* @return an iterator of the collected objects.
*/
- public Iterator iterator() {
- return collected == null ? Collections.EMPTY_LIST.iterator() : collected.iterator();
+ public Iterator<T> iterator() {
+ return collected == null ? CollectionUtils.<T> emptyList().iterator() : collected.iterator();
}
/**
@@ -101,37 +125,48 @@ public class Collector implements IQueryable {
* @throws ArrayStoreException the runtime type of the specified array is
* not a supertype of the runtime type of every collected object
*/
- public Object[] toArray(Class clazz) {
+ public T[] toArray(Class<? extends T> clazz) {
int size = collected == null ? 0 : collected.size();
- Object[] result = (Object[]) Array.newInstance(clazz, size);
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(clazz, size);
if (size != 0)
collected.toArray(result);
return result;
}
/**
- * Returns the collected objects as an immutable collection.
+ * Returns a copy of the collected objects.
*
* @return An unmodifiable collection of the collected objects
*/
- public Collection toCollection() {
- return collected == null ? Collections.EMPTY_SET : Collections.unmodifiableSet(collected);
+ public Set<T> toSet() {
+ return collected == null ? new HashSet<T>() : new HashSet<T>(collected);
}
/**
* Performs a query on this results of this collector.
*/
- public Collector query(Query query, Collector collector, IProgressMonitor monitor) {
- Iterator iter = collector == this ? toCollection().iterator() : iterator();
+ public IQueryResult<T> query(IQuery<T> query, IProgressMonitor monitor) {
+ IQueryResult<T> result;
if (monitor == null)
monitor = new NullProgressMonitor();
try {
monitor.beginTask(Messages.performing_subquery, 1);
- collector = query.perform(iter, collector);
+ result = query.perform(iterator());
monitor.worked(1);
} finally {
monitor.done();
}
- return collector;
+ return result;
+ }
+
+ /**
+ * Returns the collected objects as an immutable collection.
+ *
+ * @return An unmodifiable collection of the collected objects
+ */
+ @SuppressWarnings("unchecked")
+ public Set<T> unmodifiableSet() {
+ return collected == null ? Collections.EMPTY_SET : Collections.unmodifiableSet(collected);
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompoundQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/CompoundQuery.java
index 45cc8b66d..594738036 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompoundQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/CompoundQuery.java
@@ -9,10 +9,12 @@
* IBM Corporation - initial API and implementation
* EclipseSource - ongoing development
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
+package org.eclipse.equinox.p2.query;
+import java.lang.reflect.Array;
import java.util.*;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.equinox.internal.p2.query.QueryHelpers;
/**
* A query that combines a group of sub-queries.<P>
@@ -20,15 +22,16 @@ import java.util.*;
* In a CompoundQuery each sub-query is executed and the results are combined using
* either logical AND or logical OR operations. <P>
*
- * Clients are expected to call {@link CompoundQuery#createCompoundQuery(Query[], boolean)}
+ * Clients are expected to call {@link CompoundQuery#createCompoundQuery(IQuery[], boolean)}
* to create a concrete instance of a CompoundQuery. If all Queries are instances of
* {@link IMatchQuery} then the resulting compound query will be a MatchCompoundQuery, otherwise the
* resulting compound query will be a {@link ContextQuery}.
*
* @noextend This class is not intended to be subclassed by clients.
+ * @since 2.0
*/
-public abstract class CompoundQuery implements Query {
- protected Query[] queries;
+public abstract class CompoundQuery<T> implements ICompositeQuery<T> {
+ protected IQuery<T>[] queries;
protected boolean and;
/**
@@ -45,18 +48,23 @@ public abstract class CompoundQuery implements Query {
* @param and <code>true</code> if this query represents a logical 'and', and
* <code>false</code> if this query represents a logical 'or'.
*/
- public static CompoundQuery createCompoundQuery(Query[] queries, boolean and) {
+ public static <T> CompoundQuery<T> createCompoundQuery(IQuery<T>[] queries, boolean and) {
if (isMatchQueries(queries)) {
- return new CompoundQuery.MatchCompoundQuery(queries, and);
+ return new CompoundQuery.MatchCompoundQuery<T>(queries, and);
}
- return new CompoundQuery.ContextCompoundQuery(queries, and);
+ return new CompoundQuery.ContextCompoundQuery<T>(queries, and);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> CompoundQuery<T> createCompoundQuery(IQuery<T> query1, IQuery<T> query2, boolean and) {
+ return createCompoundQuery(new IQuery[] {query1, query2}, and);
}
/**
* Returns the queries that make up this compound query
*/
- public Query[] getQueries() {
- return queries;
+ public List<IQuery<T>> getQueries() {
+ return Arrays.asList(queries);
}
/**
@@ -69,7 +77,7 @@ public abstract class CompoundQuery implements Query {
return and;
}
- protected CompoundQuery(Query[] queries, boolean and) {
+ protected CompoundQuery(IQuery<T>[] queries, boolean and) {
this.queries = queries;
this.and = and;
}
@@ -77,9 +85,9 @@ public abstract class CompoundQuery implements Query {
/**
* @param queries
*/
- private static boolean isMatchQueries(Query[] queries) {
+ private static boolean isMatchQueries(IQuery<?>[] queries) {
for (int i = 0; i < queries.length; i++) {
- if (!(queries[i] instanceof IMatchQuery)) {
+ if (!(queries[i] instanceof IMatchQuery<?>)) {
return false;
}
}
@@ -104,15 +112,15 @@ public abstract class CompoundQuery implements Query {
/**
* The compound query instantiated when all queries are Match Queries.
*/
- private static class MatchCompoundQuery extends CompoundQuery implements IMatchQuery {
+ private static class MatchCompoundQuery<T> extends CompoundQuery<T> implements IMatchQuery<T> {
- protected MatchCompoundQuery(Query[] queries, boolean and) {
+ protected MatchCompoundQuery(IQuery<T>[] queries, boolean and) {
super(queries, and);
}
- public boolean isMatch(Object candidate) {
+ public boolean isMatch(T candidate) {
for (int i = 0; i < queries.length; i++) {
- boolean valid = ((IMatchQuery) queries[i]).isMatch(candidate);
+ boolean valid = ((IMatchQuery<T>) queries[i]).isMatch(candidate);
// if we are OR'ing then the first time we find a requirement that is met, return success
if (valid && !and)
return true;
@@ -130,11 +138,12 @@ public abstract class CompoundQuery implements Query {
* Performs this query on the given iterator, passing all objects in the iterator
* that match the criteria of this query to the given result.
*/
- public final Collector perform(Iterator iterator, Collector result) {
+ public final IQueryResult<T> perform(Iterator<T> iterator) {
prePerform();
+ Collector<T> result = new Collector<T>();
try {
while (iterator.hasNext()) {
- Object candidate = iterator.next();
+ T candidate = iterator.next();
if (isMatch(candidate))
if (!result.accept(candidate))
break;
@@ -147,13 +156,13 @@ public abstract class CompoundQuery implements Query {
public void prePerform() {
for (int i = 0; i < queries.length; i++) {
- ((IMatchQuery) queries[i]).prePerform();
+ ((IMatchQuery<T>) queries[i]).prePerform();
}
}
public void postPerform() {
for (int i = 0; i < queries.length; i++) {
- ((IMatchQuery) queries[i]).postPerform();
+ ((IMatchQuery<T>) queries[i]).postPerform();
}
}
}
@@ -162,57 +171,82 @@ public abstract class CompoundQuery implements Query {
* The compound query instantiated when any of the queries are not
* match queries.
*/
- private static class ContextCompoundQuery extends CompoundQuery {
+ private static class ContextCompoundQuery<T> extends CompoundQuery<T> {
- protected ContextCompoundQuery(Query[] queries, boolean and) {
+ protected ContextCompoundQuery(IQuery<T>[] queries, boolean and) {
super(queries, and);
}
/*
* A collector that takes the set to puts the elements in.
*/
- class SetCollector extends Collector {
- Set s = null;
+ static class SetCollector<T> implements IQueryResult<T> {
+ private final Set<T> s;
- public SetCollector(Set s) {
+ public SetCollector(Set<T> s) {
this.s = s;
}
- public boolean accept(Object object) {
- s.add(object);
- return true;
+ public boolean isEmpty() {
+ return s.isEmpty();
}
- }
- public Collector perform(Iterator iterator, Collector result) {
- if (queries.length < 1)
- return result;
+ public Iterator<T> iterator() {
+ return s.iterator();
+ }
- Collection data = new LinkedList();
+ @SuppressWarnings("unchecked")
+ public T[] toArray(Class<? extends T> clazz) {
+ return s.toArray((T[]) Array.newInstance(clazz, s.size()));
+ }
- while (iterator.hasNext()) {
- data.add(iterator.next());
+ public IQueryResult<T> query(IQuery<T> query, IProgressMonitor monitor) {
+ return query.perform(iterator());
}
- Set[] resultSets = new Set[queries.length];
- for (int i = 0; i < queries.length; i++) {
- resultSets[i] = new HashSet();
- queries[i].perform(data.iterator(), new SetCollector(resultSets[i]));
+ public Set<T> toSet() {
+ return new HashSet<T>(s);
}
- Set set = resultSets[0];
- for (int i = 1; i < resultSets.length; i++) {
- if (isAnd())
- set.retainAll(resultSets[i]);
- else
- set.addAll(resultSets[i]);
+ public Set<T> unmodifiableSet() {
+ return Collections.unmodifiableSet(s);
}
+ }
- Iterator resultIterator = set.iterator();
- boolean gatherResults = true;
- while (resultIterator.hasNext() && gatherResults)
- gatherResults = result.accept(resultIterator.next());
- return result;
+ public IQueryResult<T> perform(Iterator<T> iterator) {
+ if (queries.length < 1)
+ return Collector.emptyCollector();
+
+ if (queries.length == 1)
+ return queries[0].perform(iterator);
+
+ Collection<T> data = new ArrayList<T>();
+ while (iterator.hasNext())
+ data.add(iterator.next());
+
+ Set<T> result;
+ if (isAnd()) {
+ result = queries[0].perform(data.iterator()).unmodifiableSet();
+ for (int i = 1; i < queries.length && result.size() > 0; i++) {
+ HashSet<T> retained = new HashSet<T>();
+ Iterator<T> itor = queries[i].perform(data.iterator()).iterator();
+ while (itor.hasNext()) {
+ T nxt = itor.next();
+ if (result.contains(nxt))
+ retained.add(nxt);
+ }
+ result = retained;
+ }
+ } else {
+ result = queries[0].perform(data.iterator()).toSet();
+ for (int i = 1; i < queries.length; i++) {
+ Iterator<T> itor = queries[i].perform(data.iterator()).iterator();
+ while (itor.hasNext()) {
+ result.add(itor.next());
+ }
+ }
+ }
+ return new SetCollector<T>(result);
}
}
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompoundQueryable.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/CompoundQueryable.java
index ff7484c73..bbfd2bd20 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompoundQueryable.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/CompoundQueryable.java
@@ -7,55 +7,79 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.p2.query;
import java.lang.reflect.Array;
import java.util.*;
import org.eclipse.core.runtime.*;
+import org.eclipse.equinox.internal.p2.core.helpers.CollectionUtils;
/**
* A queryable that holds a number of other IQueryables and provides
* a mechanism for querying the entire set.
+ * @since 2.0
*/
-public class CompoundQueryable implements IQueryable {
+public class CompoundQueryable<T> implements IQueryable<T> {
- private IQueryable[] queryables;
+ private IQueryable<T>[] queryables;
- public CompoundQueryable(IQueryable[] queryables) {
+ private CompoundQueryable(IQueryable<T>[] queryables) {
this.queryables = queryables;
}
- public Collector query(Query query, Collector collector, IProgressMonitor monitor) {
+ @SuppressWarnings("unchecked")
+ public CompoundQueryable(Collection<? extends IQueryable<T>> queryables) {
+ this(queryables.toArray(new IQueryable[queryables.size()]));
+ }
+
+ @SuppressWarnings("unchecked")
+ public CompoundQueryable(IQueryable<T> q) {
+ this(new IQueryable[] {q});
+ }
+
+ @SuppressWarnings("unchecked")
+ public CompoundQueryable(IQueryable<T> q1, IQueryable<T> q2) {
+ this(new IQueryable[] {q1, q2});
+ }
+
+ @SuppressWarnings("unchecked")
+ public CompoundQueryable(IQueryable<T> q1, IQueryable<T> q2, IQueryable<T> q3) {
+ this(new IQueryable[] {q1, q2, q3});
+ }
+
+ public IQueryResult<T> query(IQuery<T> query, IProgressMonitor monitor) {
+ IQueryResult<T> subResults = null;
if (monitor == null) {
monitor = new NullProgressMonitor();
}
- boolean isMatchQuery = query instanceof IMatchQuery;
- Collector results = collector;
+ boolean isMatchQuery = query instanceof IMatchQuery<?>;
int totalWork = isMatchQuery ? queryables.length : queryables.length + 1;
try {
SubMonitor subMonitor = SubMonitor.convert(monitor, totalWork * 10);
+ Collector<T> results;
if (!isMatchQuery) {
// If it is not a match query, then collect the results
// as a list, we will query this list for the final results
- results = new ListCollector();
- }
+ results = new ListCollector<T>();
+ } else
+ results = new Collector<T>();
+
for (int i = 0; i < queryables.length; i++) {
if (subMonitor.isCanceled())
break;
- results = queryables[i].query(query, results, subMonitor.newChild(10));
+ subResults = queryables[i].query(query, subMonitor.newChild(10));
+ results.addAll(subResults);
}
- if (!isMatchQuery) {
- // If it is not a MatchQuery then we must query the results.
- collector = results.query(query, collector, subMonitor.newChild(10));
- } else
- collector = results;
+ if (isMatchQuery)
+ return results;
+
+ // If it is not a MatchQuery then we must query the results.
+ return results.query(query, subMonitor.newChild(10));
} finally {
monitor.done();
}
-
- return collector;
}
/**
@@ -70,16 +94,16 @@ public class CompoundQueryable implements IQueryable {
* @noextend This class is not intended to be subclassed by clients.
*
*/
- public class ListCollector extends Collector {
- private List collected;
+ public static class ListCollector<T> extends Collector<T> {
+ private List<T> collected;
public ListCollector() {
super();
}
- protected Collection getCollection() {
+ protected Collection<T> getCollection() {
if (collected == null)
- collected = new ArrayList();
+ collected = new ArrayList<T>();
return collected;
}
@@ -87,17 +111,18 @@ public class CompoundQueryable implements IQueryable {
return collected == null || collected.isEmpty();
}
- public Object[] toArray(Class clazz) {
+ @SuppressWarnings("unchecked")
+ public T[] toArray(Class<? extends T> clazz) {
int size = collected == null ? 0 : collected.size();
- Object[] result = (Object[]) Array.newInstance(clazz, size);
+ T[] result = (T[]) Array.newInstance(clazz, size);
if (size != 0)
collected.toArray(result);
return result;
}
- public boolean accept(Object object) {
+ public boolean accept(T object) {
if (collected == null)
- collected = new ArrayList();
+ collected = new ArrayList<T>();
collected.add(object);
return true;
}
@@ -107,12 +132,12 @@ public class CompoundQueryable implements IQueryable {
*
* @return An unmodifiable collection of the collected objects
*/
- public Collection toCollection() {
- return collected == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(collected);
+ public Set<T> toSet() {
+ return collected == null ? new HashSet<T>() : new HashSet<T>(collected);
}
- public Iterator iterator() {
- return collected == null ? Collections.EMPTY_LIST.iterator() : collected.iterator();
+ public Iterator<T> iterator() {
+ return collected == null ? CollectionUtils.<T> emptyList().iterator() : collected.iterator();
}
public int size() {
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/ContextQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ContextQuery.java
index 532f9e691..f65aa8a52 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/ContextQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ContextQuery.java
@@ -7,10 +7,10 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
+package org.eclipse.equinox.p2.query;
import java.util.Iterator;
+import org.eclipse.equinox.internal.p2.query.QueryHelpers;
/**
* ContextQuery is the abstract superclass for Queries that require the entire
@@ -22,7 +22,7 @@ import java.util.Iterator;
* need for a non-transitive query, please see:
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=261403
* <p>
- * Users of this query must call {@link #perform(Iterator, Collector)} to compute
+ * Users of this query must call {@link #perform(Iterator)} to compute
* the results. <P>
* This class may be subclassed by clients. Subclasses should specify the type
* of object they support querying on. Subclasses are also encouraged to clearly
@@ -30,19 +30,18 @@ import java.util.Iterator;
* computation, to allow {@link IQueryable} implementations to optimize their
* execution of the query. <P>
*
+ * @since 2.0
*/
-public abstract class ContextQuery implements Query {
+public abstract class ContextQuery<T> implements IQuery<T> {
/**
* Evaluates the query for a specific input.
*
* @param iterator The elements for which to evaluate the query on
- * @param result A collector to collect the results. For each element accepted
- * by the query,{@link Collector#accept(Object)} must be called.
* @return The results of the query. The collector returned must be
* the collector passed in.
*/
- public abstract Collector perform(Iterator iterator, Collector result);
+ public abstract IQueryResult<T> perform(Iterator<T> iterator);
/**
* Gets the ID for this Query.
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ICompositeQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ICompositeQuery.java
new file mode 100644
index 000000000..1b0baeae5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/ICompositeQuery.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+* Copyright (c) 2009, 2010 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
+*
+* Contributors:
+* EclipseSource - initial API and implementation
+******************************************************************************/
+package org.eclipse.equinox.p2.query;
+
+import java.util.List;
+
+/**
+ * A query that contains a number of sub queries. All queries that support sub-queries
+ * should implement this interface so clients can access the sub queries.
+ * @since 2.0
+ */
+public interface ICompositeQuery<T> extends IQuery<T> {
+
+ /**
+ * Returns all the child queries of a CompositeQuery.
+ * @return All the child queries.
+ */
+ public List<IQuery<T>> getQueries();
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IMatchQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IMatchQuery.java
index 6a3830611..b25b044b6 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IMatchQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IMatchQuery.java
@@ -1,5 +1,5 @@
/*******************************************************************************
-* Copyright (c) 2009 EclipseSource and others. All rights reserved. This
+* Copyright (c) 2009, 2010 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
@@ -7,7 +7,7 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.p2.query;
/**
* A query in which the elements can be evaluated by calling isMatch on. Each
@@ -15,9 +15,11 @@ package org.eclipse.equinox.internal.provisional.p2.metadata.query;
* can be evaluated in parallel as each call {@link #isMatch(Object)} is mutually
* exclusive from all other calls. <P>
*
- * @spi Clients should not implement this interface, but rather extend {@link MatchQuery}.
+ * @noimplement This interface is not intended to be implemented by clients. Clients
+ * creating custom queries must extend {@link MatchQuery} instead.
+ * @since 2.0
*/
-public interface IMatchQuery extends Query {
+public interface IMatchQuery<T> extends IQuery<T> {
/**
* Returns whether the given object satisfies the parameters of this query.
@@ -28,7 +30,7 @@ public interface IMatchQuery extends Query {
*
* @noreference This method is not intended to be referenced by clients.
*/
- public boolean isMatch(Object candidate);
+ public boolean isMatch(T candidate);
/**
* Execute any pre-processing that must be done before this query is performed against
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/Query.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQuery.java
index a9acfdfa2..9c61de05f 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/Query.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQuery.java
@@ -9,7 +9,7 @@
* IBM Corporation - initial API and implementation
* EclipseSource - ongoing development
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.p2.query;
import java.util.Iterator;
@@ -18,24 +18,20 @@ import java.util.Iterator;
* The superclass of all queries that can be performed on an {@link IQueryable}.
* <p>
*
- * <B>NOTE: This interface does not follow the proper naming convention. It should
- * be IQuery, however, for historic reasons it is Query. This is likely to change.</B>
- *
* @noimplement This interface is not intended to be implemented by clients.
* @noextend This interface is not intended to be extended by clients.
+ * @since 2.0
*/
-public interface Query {
+public interface IQuery<T> {
/**
* Evaluates the query for a specific input.
*
* @param iterator The elements for which to evaluate the query on
- * @param result A collector to collect the results. For each element accepted
- * by the query,{@link Collector#accept(Object)} must be called.
* @return The results of the query. The collector returned must be
* the collector passed in.
*/
- public abstract Collector perform(Iterator iterator, Collector result);
+ public abstract IQueryResult<T> perform(Iterator<T> iterator);
/**
* Gets the ID for this Query.
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java
new file mode 100644
index 000000000..12e0cbf1c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+* Copyright (c) 2009 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
+*
+* Contributors:
+* EclipseSource - initial API and implementation
+******************************************************************************/
+package org.eclipse.equinox.p2.query;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * An IQueryResult represents the results of a query.
+ * @since 2.0
+ *
+ */
+public interface IQueryResult<T> extends IQueryable<T> {
+ /**
+ * Returns whether this QueryResult is empty.
+ * @return <code>true</code> if this QueryResult has accepted any results,
+ * and <code>false</code> otherwise.
+ */
+ public boolean isEmpty();
+
+ /**
+ * Returns an iterator on the collected objects.
+ *
+ * @return an iterator of the collected objects.
+ */
+ public Iterator<T> iterator();
+
+ /**
+ * Returns the collected objects as an array
+ *
+ * @param clazz The type of array to return
+ * @return The array of results
+ * @throws ArrayStoreException the runtime type of the specified array is
+ * not a supertype of the runtime type of every collected object
+ */
+ public T[] toArray(Class<? extends T> clazz);
+
+ /**
+ * Creates a new Set copy with the contents of this query result. The
+ * copy can be altered without any side effects on its origin.
+ * @return A detached copy of the result.
+ */
+ public Set<T> toSet();
+
+ /**
+ * Returns a Set backed by this query result. The set is immutable.
+ * @return A Set backed by this query result.
+ */
+ public Set<T> unmodifiableSet();
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IQueryable.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryable.java
index 3389d5182..22b4d8bd7 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/IQueryable.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryable.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2007, 2008 IBM Corporation and others.
+ * Copyright (c) 2007, 2010 IBM Corporation 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
@@ -8,8 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
+package org.eclipse.equinox.p2.query;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -17,8 +16,9 @@ import org.eclipse.core.runtime.IProgressMonitor;
* An IQueryable contains objects, and is able to perform queries on those objects.
* <p>
* This interface may be implemented by clients.
+ * @since 2.0
*/
-public interface IQueryable {
+public interface IQueryable<T> {
/**
* Performs a query, passing any objects that satisfy the
* query to the provided collector.
@@ -28,10 +28,9 @@ public interface IQueryable {
* </p>
*
* @param query The query to perform
- * @param collector Collects the results of the query
* @param monitor a progress monitor, or <code>null</code> if progress
* reporting is not desired
* @return The collector argument
*/
- public Collector query(Query query, Collector collector, IProgressMonitor monitor);
+ public IQueryResult<T> query(IQuery<T> query, IProgressMonitor monitor);
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/LimitQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/LimitQuery.java
new file mode 100644
index 000000000..6da75ca1f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/LimitQuery.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+* Copyright (c) 2009 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
+*
+* Contributors:
+* EclipseSource - initial API and implementation
+******************************************************************************/
+package org.eclipse.equinox.p2.query;
+
+import java.util.*;
+
+/**
+ * A limit query can be used to limit the number of query results returned. Once
+ * the limit is reached, the query is terminated.
+ * @since 2.0
+ */
+public class LimitQuery<T> extends ContextQuery<T> implements ICompositeQuery<T> {
+
+ private final IQuery<T> query;
+ private final int limit;
+
+ public LimitQuery(IQuery<T> query, int limit) {
+ this.query = query;
+ this.limit = limit;
+ }
+
+ public IQueryResult<T> perform(Iterator<T> iterator) {
+ if (limit == 0)
+ return Collector.emptyCollector();
+
+ int count = 0;
+ Collector<T> result = new Collector<T>();
+ if (query instanceof IMatchQuery<?>) {
+ IMatchQuery<T> matchQuery = (IMatchQuery<T>) query;
+ while (iterator.hasNext()) {
+ T candidate = iterator.next();
+ if (matchQuery.isMatch(candidate)) {
+ result.accept(candidate);
+ if (++count >= limit)
+ break;
+ }
+ }
+ } else {
+ iterator = query.perform(iterator).iterator();
+ while (++count <= limit && iterator.hasNext())
+ result.accept(iterator.next());
+ }
+ return result;
+ }
+
+ public List<IQuery<T>> getQueries() {
+ return Collections.singletonList(query);
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/MatchQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/MatchQuery.java
index 86b647663..f8e4e8a2d 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/MatchQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/MatchQuery.java
@@ -8,10 +8,10 @@
* EclipseSource - initial API and implementation
* IBM Corporation - ongoing development
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
-
+package org.eclipse.equinox.p2.query;
import java.util.Iterator;
+import org.eclipse.equinox.internal.p2.query.QueryHelpers;
/**
* This class represents the superclass of most of p2's queries. Every element
@@ -24,8 +24,9 @@ import java.util.Iterator;
* specify their match algorithm, and expose the parameters involved in the match
* computation, to allow {@link IQueryable} implementations to optimize their
* execution of the query.
+ * @since 2.0
*/
-public abstract class MatchQuery implements IMatchQuery {
+public abstract class MatchQuery<T> implements IMatchQuery<T> {
/**
* Returns whether the given object satisfies the parameters of this query.
@@ -35,9 +36,9 @@ public abstract class MatchQuery implements IMatchQuery {
* of this query, and <code>false</code> otherwise
*
* @noreference This method is not intended to be referenced by clients.
- * Clients should call {@link #perform(Iterator, Collector)}
+ * Clients should call {@link #perform(Iterator)}
*/
- public abstract boolean isMatch(Object candidate);
+ public abstract boolean isMatch(T candidate);
/**
* Gets the ID for this Query.
@@ -58,12 +59,13 @@ public abstract class MatchQuery implements IMatchQuery {
* Performs this query on the given iterator, passing all objects in the iterator
* that match the criteria of this query to the given result.
*/
- public final Collector perform(Iterator iterator, Collector result) {
+ public final IQueryResult<T> perform(Iterator<T> iterator) {
+ Collector<T> result = new Collector<T>();
prePerform();
try {
while (iterator.hasNext()) {
- Object candidate = iterator.next();
- if (isMatch(candidate))
+ T candidate = iterator.next();
+ if (candidate != null && isMatch(candidate))
if (!result.accept(candidate))
break;
}
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompositeQuery.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/PipedQuery.java
index a4c88a629..07147a1cf 100644
--- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/provisional/p2/metadata/query/CompositeQuery.java
+++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/PipedQuery.java
@@ -7,23 +7,29 @@
* Contributors:
* EclipseSource - initial API and implementation
******************************************************************************/
-package org.eclipse.equinox.internal.provisional.p2.metadata.query;
+package org.eclipse.equinox.p2.query;
-
-import java.util.Iterator;
+import java.util.*;
+import org.eclipse.equinox.internal.p2.query.QueryHelpers;
/**
- * A Composite Query is an aggregate query in which each sub-query
+ * A PipedQuery is an aggregate query in which each sub-query
* is executed in succession. The results from the ith sub-query
* are piped as input into the i+1th sub-query.
+ * @since 2.0
*/
-public class CompositeQuery implements Query {
- protected Query[] queries;
+public class PipedQuery<T> implements ICompositeQuery<T> {
+ protected final IQuery<T>[] queries;
- public CompositeQuery(Query[] queries) {
+ public PipedQuery(IQuery<T>[] queries) {
this.queries = queries;
}
+ @SuppressWarnings("unchecked")
+ public PipedQuery(IQuery<T> query1, IQuery<T> query2) {
+ this(new IQuery[] {query1, query2});
+ }
+
/**
* Gets the ID for this Query.
*/
@@ -40,25 +46,21 @@ public class CompositeQuery implements Query {
}
/**
- * Set the queries of this composite. This is needed to allow subclasses of
- * CompsiteQuery to set the queries in a constructor
+ * Returns the queries that make up this compound query
*/
- protected final void setQueries(Query[] queries) {
- this.queries = queries;
+ public List<IQuery<T>> getQueries() {
+ return Arrays.asList(queries);
}
- public Collector perform(Iterator iterator, Collector result) {
- Collector collector;
- Iterator iter = iterator;
- for (int i = 0; i < queries.length; i++) {
- // Take the results of the previous query and using them
- // to drive the next one (i.e. composing queries)
- collector = queries[i].perform(iter, new Collector());
- iter = collector.iterator();
+ public IQueryResult<T> perform(Iterator<T> iterator) {
+ IQueryResult<T> last = Collector.emptyCollector();
+ if (queries.length > 0) {
+ last = queries[0].perform(iterator);
+ for (int i = 1; i < queries.length; i++)
+ // Take the results of the previous query and using them
+ // to drive the next one (i.e. composing queries)
+ last = queries[i].perform(last.iterator());
}
- boolean gatherResults = true;
- while (iter.hasNext() && gatherResults)
- gatherResults = result.accept(iter.next());
- return result;
+ return last;
}
}

Back to the top