diff options
author | Mickael Istria | 2013-05-03 14:24:31 +0000 |
---|---|---|
committer | Pascal Rapicault | 2013-05-05 21:56:57 +0000 |
commit | 98da82bec04ad8547892cb4a9e7da1ce9bb1b92c (patch) | |
tree | a0751f206bf4124aa7c469f79eb702364f47be66 | |
parent | e813506a5b81b0d4ba9dcc6371d2ddb3ec940ed9 (diff) | |
download | rt.equinox.p2-I20130506-2000.tar.gz rt.equinox.p2-I20130506-2000.tar.xz rt.equinox.p2-I20130506-2000.zip |
Bug 406902: Allow nested category in category.xmlI20130506-2000I20130505-2000
5 files changed, 202 insertions, 55 deletions
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java index 59b6a0afd..0cd740547 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/updatesite/CategoryXMLActionTest.java @@ -1,4 +1,4 @@ -/******************************************************************************* +/******************************************************************************* * 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 @@ -145,4 +145,41 @@ public class CategoryXMLActionTest extends AbstractProvisioningTest { assertEquals("2.0", 1, categoryMembersSet.size()); assertEquals("2.1", "test.bundle", categoryMembersSet.iterator().next().getId()); } + + public void testNestedInCategory() throws Exception { + PublisherInfo info = new PublisherInfo(); + + info.setMetadataRepository(metadataRepository); + siteLocation = TestData.getFile("updatesite", "CategoryXMLActionTest/categoryNested.xml").toURI(); + FeaturesAction featuresAction = new FeaturesAction(new File[] {TestData.getFile("updatesite", "CategoryXMLActionTest")}); + BundlesAction bundlesAction = new BundlesAction(new File[] {TestData.getFile("updatesite", "CategoryXMLActionTest")}); + MergeResultsAction publishAction = new MergeResultsAction(new IPublisherAction[] {featuresAction, bundlesAction}, IPublisherResult.MERGE_ALL_NON_ROOT); + publishAction.perform(info, actionResult, new NullProgressMonitor()); + + CategoryXMLAction action = new CategoryXMLAction(siteLocation, null); + action.perform(info, actionResult, getMonitor()); + + IQueryResult result = actionResult.query(QueryUtil.createIUCategoryQuery(), new NullProgressMonitor()); + assertEquals("1.0", 2, queryResultSize(result)); + IInstallableUnit rootCategoryIu = null; + for (Object item : result) { + if (((IInstallableUnit) item).getId().endsWith("Root Category")) { + rootCategoryIu = (IInstallableUnit) item; + } + } + assertNotNull("1.1", rootCategoryIu); + + IQuery<IInstallableUnit> rootCategoryMembersQuery = QueryUtil.createIUCategoryMemberQuery(rootCategoryIu); + IQueryResult<IInstallableUnit> rootCategoryMembers = actionResult.query(rootCategoryMembersQuery, new NullProgressMonitor()); + Set<IInstallableUnit> rootCategoryMembersSet = rootCategoryMembers.toUnmodifiableSet(); + assertEquals("2.0", 1, rootCategoryMembersSet.size()); + IInstallableUnit nestedCategory = rootCategoryMembersSet.iterator().next(); + assertTrue("2.1", nestedCategory.getId().endsWith("Nested Category")); + + IQuery<IInstallableUnit> nestedCategoryMemberQuery = QueryUtil.createIUCategoryMemberQuery(nestedCategory); + IQueryResult<IInstallableUnit> nestedCategoryMembers = actionResult.query(nestedCategoryMemberQuery, new NullProgressMonitor()); + Set<IInstallableUnit> nestedCategoryMembersSet = nestedCategoryMembers.toUnmodifiableSet(); + assertEquals("3.0", 1, nestedCategoryMembersSet.size()); + assertEquals("3.1", "test.bundle", nestedCategoryMembersSet.iterator().next().getId()); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryNested.xml b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryNested.xml new file mode 100644 index 000000000..9b9ff2379 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/testData/updatesite/CategoryXMLActionTest/categoryNested.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<site associateSitesURL="associateSites.xml" mirrorsURL="http://www.eclipse.org/downloads/download.php?file=/eclipse/updates/3.4&format=xml"> + <category-def label="Root Category" name="Root Category"/> + <category-def label="Nested Category" name="Nested Category"> + <category name="Root Category"/> + </category-def> + <bundle url="plugins/test.bundle_1.0.0.jar" id="test.bundle" version="1.0.0"> + <category name="Nested Category"/> + </bundle> +</site> diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java index 77fb88c32..fa06c7322 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/CategoryParser.java @@ -4,7 +4,7 @@ * 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 *******************************************************************************/ @@ -74,7 +74,7 @@ public class CategoryParser extends DefaultHandler { private MultiStatus status; /* - * + * */ private static void debug(String s) { Tracing.debug("CategoryParser: " + s); //$NON-NLS-1$ @@ -397,6 +397,9 @@ public class CategoryParser extends DefaultHandler { if (elementName.equals(DESCRIPTION)) { stateStack.push(new Integer(STATE_DESCRIPTION_CATEGORY_DEF)); processInfo(attributes); + } else if (elementName.equals(CATEGORY)) { + stateStack.push(new Integer(STATE_CATEGORY)); + processCategory(attributes); } else internalErrorUnknownTag(NLS.bind(Messages.DefaultSiteParser_UnknownElement, (new String[] {elementName, getState(currentState())}))); } @@ -487,14 +490,14 @@ public class CategoryParser extends DefaultHandler { } /* - * + * */ private void internalError(String message) { error(new Status(IStatus.ERROR, PLUGIN_ID, IStatus.OK, message, null)); } /* - * + * */ private void internalErrorUnknownTag(String msg) { stateStack.push(new Integer(STATE_IGNORED_ELEMENT)); @@ -509,7 +512,7 @@ public class CategoryParser extends DefaultHandler { } /* - * + * */ private void logStatus(SAXParseException ex) { String name = ex.getSystemId(); @@ -531,7 +534,7 @@ public class CategoryParser extends DefaultHandler { /** * Parses the specified input steam and constructs a site model. * The input stream is not closed as part of this operation. - * + * * @param in input stream * @return site model * @exception SAXException @@ -556,7 +559,7 @@ public class CategoryParser extends DefaultHandler { throw new SAXException(NLS.bind(Messages.DefaultSiteParser_WrongParsingStack, (new String[] {stack}))); } - /* + /* * process archive info */ private void processArchive(Attributes attributes) { @@ -565,7 +568,7 @@ public class CategoryParser extends DefaultHandler { debug("End processing Archive"); //$NON-NLS-1$ } - /* + /* * process the Category info */ private void processCategory(Attributes attributes) { @@ -578,13 +581,15 @@ public class CategoryParser extends DefaultHandler { ((SiteBundle) obj).addCategoryName(category); } else if (obj instanceof SiteIU) { ((SiteIU) obj).addCategoryName(category); + } else if (obj instanceof SiteCategory) { + ((SiteCategory) obj).addCategoryName(category); } if (Tracing.DEBUG_GENERATOR_PARSING) debug("End processing Category: name:" + category); //$NON-NLS-1$ } - /* + /* * process category def info */ private void processCategoryDef(Attributes attributes) { @@ -603,7 +608,7 @@ public class CategoryParser extends DefaultHandler { debug("End processing CategoryDef: name:" + name + " label:" + label); //$NON-NLS-1$ //$NON-NLS-2$ } - /* + /* * process feature info */ private void processFeature(Attributes attributes) { @@ -632,7 +637,7 @@ public class CategoryParser extends DefaultHandler { debug("End Processing Feature Tag: id:" + id + " version:" + ver); //$NON-NLS-1$ //$NON-NLS-2$ } - /* + /* * process bundle info */ private void processBundle(Attributes attributes) { @@ -661,7 +666,7 @@ public class CategoryParser extends DefaultHandler { debug("End Processing Bundle Tag: id:" + id + " version:" + ver); //$NON-NLS-1$ //$NON-NLS-2$ } - /* + /* * process IU info */ private void processIU(Attributes attributes) { @@ -684,7 +689,7 @@ public class CategoryParser extends DefaultHandler { debug("End processing iu."); //$NON-NLS-1$ } - /* + /* * process expression info */ private void processExpression(Attributes attributes) { @@ -692,10 +697,10 @@ public class CategoryParser extends DefaultHandler { iu.setQueryType(attributes.getValue("type")); //$NON-NLS-1$ if (Tracing.DEBUG_GENERATOR_PARSING) - debug("End processing Expression: " + iu.getQueryType()); //$NON-NLS-1$ + debug("End processing Expression: " + iu.getQueryType()); //$NON-NLS-1$ } - /* + /* * process query info */ private void processQuery(Attributes attributes) { @@ -704,7 +709,7 @@ public class CategoryParser extends DefaultHandler { debug("End processing Query."); //$NON-NLS-1$ } - /* + /* * process param info */ private void processParam(Attributes attributes) { @@ -712,7 +717,7 @@ public class CategoryParser extends DefaultHandler { debug("End processing Param."); //$NON-NLS-1$ } - /* + /* * process URL info with element text */ private void processInfo(Attributes attributes) { @@ -726,7 +731,7 @@ public class CategoryParser extends DefaultHandler { objectStack.push(inf); } - /* + /* * process site info */ private void processSite(Attributes attributes) { diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteCategory.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteCategory.java index 2a76d3940..a98529bdc 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteCategory.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteCategory.java @@ -4,7 +4,7 @@ * 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 *******************************************************************************/ @@ -16,7 +16,7 @@ import java.util.*; /** * A category in an update site. - * + * * Based on org.eclipse.update.core.model.CategoryModel. */ public class SiteCategory { @@ -25,11 +25,12 @@ public class SiteCategory { private String description; private String label; private String name; + private List<String> categoryNames; private Map<Locale, Map<String, String>> localizations; /** * Returns a comparator for category models. - * + * * @return comparator * @since 2.0 */ @@ -55,7 +56,7 @@ public class SiteCategory { /** * Creates an uninitialized model object. - * + * * @since 2.0 */ public SiteCategory() { @@ -64,7 +65,7 @@ public class SiteCategory { /** * Compare two category models for equality. - * + * * @see Object#equals(Object) * @since 2.0 */ @@ -79,7 +80,7 @@ public class SiteCategory { /** * Retrieve the detailed category description - * + * * @return category description, or <code>null</code>. * @since 2.0 */ @@ -89,7 +90,7 @@ public class SiteCategory { /** * Retrieve the non-localized displayable label for the category. - * + * * @return non-localized displayable label, or <code>null</code>. * @since 2.0 */ @@ -100,7 +101,7 @@ public class SiteCategory { /** * Gets the localizations for the site as a map from locale * to the set of translated properties for that locale. - * + * * @return a map from locale to property set * @since 3.4 */ @@ -110,7 +111,7 @@ public class SiteCategory { /** * Retrieve the name of the category. - * + * * @return category name, or <code>null</code>. * @since 2.0 */ @@ -120,7 +121,7 @@ public class SiteCategory { /** * Compute hash code for category model. - * + * * @see Object#hashCode() * @since 2.0 */ @@ -130,11 +131,11 @@ public class SiteCategory { /** * Resolve the model object. - * Any URL strings in the model are resolved relative to the + * Any URL strings in the model are resolved relative to the * base URL argument. Any translatable strings in the model that are - * specified as translation keys are localized using the supplied + * specified as translation keys are localized using the supplied * resource bundle. - * + * * @param base URL * @param bundleURL resource bundle URL * @exception MalformedURLException @@ -151,7 +152,7 @@ public class SiteCategory { /** * Sets the category description. * Throws a runtime exception if this object is marked read-only. - * + * * @param description category description * @since 2.0 */ @@ -162,7 +163,7 @@ public class SiteCategory { /** * Sets the category displayable label. * Throws a runtime exception if this object is marked read-only. - * + * * @param label displayable label, or resource key * @since 2.0 */ @@ -173,7 +174,7 @@ public class SiteCategory { /** * Sets the localizations for the site as a map from locale * to the set of translated properties for that locale. - * + * * @param localizations as a map from locale to property set * @since 3.4 */ @@ -184,7 +185,7 @@ public class SiteCategory { /** * Sets the category name. * Throws a runtime exception if this object is marked read-only. - * + * * @param name category name * @since 2.0 */ @@ -192,4 +193,31 @@ public class SiteCategory { this.name = name; } + /** + * Adds the name of a category this bundle belongs to. + * Throws a runtime exception if this object is marked read-only. + * + * @param categoryName category name + */ + public void addCategoryName(String categoryName) { + if (this.categoryNames == null) { + this.categoryNames = new ArrayList<String>(); + } + if (!this.categoryNames.contains(categoryName)) { + this.categoryNames.add(categoryName); + } + } + + /** + * Returns the names of categories the referenced feature belongs to. + * + * @return an array of names, or an empty array. + */ + public String[] getCategoryNames() { + if (categoryNames == null) + return new String[0]; + + return categoryNames.toArray(new String[0]); + } + } diff --git a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java index d63a1aa34..1a06342e4 100644 --- a/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java +++ b/bundles/org.eclipse.equinox.p2.updatesite/src/org/eclipse/equinox/internal/p2/updatesite/SiteXMLAction.java @@ -3,8 +3,8 @@ * 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: + * + * Contributors: * Code 9 - initial API and implementation * IBM - ongoing development * Sonatype, Inc. - transport split @@ -62,7 +62,7 @@ public class SiteXMLAction extends AbstractPublisherAction { /** * Creates a SiteXMLAction from an Update site with an optional qualifier to use for category names - * @param updateSite The update site + * @param updateSite The update site * @param categoryQualifier The qualifier to prepend to categories. This qualifier is used * to ensure that the category IDs are unique between update sites. If <b>null</b> a default * qualifier will be generated @@ -417,35 +417,106 @@ public class SiteXMLAction extends AbstractPublisherAction { /** * Generates IUs corresponding to update site categories. - * @param categoriesToFeatures Map of SiteCategory ->Set (Feature IUs in that category). + * @param categoriesToIUs Map of SiteCategory ->Set (Feature IUs in that category). * @param result The generator result being built */ - protected void generateCategoryIUs(Map<SiteCategory, Set<IInstallableUnit>> categoriesToFeatures, IPublisherResult result) { - for (SiteCategory category : categoriesToFeatures.keySet()) { - result.addIU(createCategoryIU(category, categoriesToFeatures.get(category), null), IPublisherResult.NON_ROOT); + protected void generateCategoryIUs(Map<SiteCategory, Set<IInstallableUnit>> categoriesToIUs, IPublisherResult result) { + Map<String, SiteCategory> nameToCategory = new HashMap<String, SiteCategory>(); + for (SiteCategory category : this.updateSite.getSite().getCategories()) { + nameToCategory.put(category.getName(), category); + } + Map<SiteCategory, Set<SiteCategory>> categoryToNestedCategories = new HashMap<SiteCategory, Set<SiteCategory>>(); + for (SiteCategory category : this.updateSite.getSite().getCategories()) { + for (String parentCategoryName : category.getCategoryNames()) { + SiteCategory parentCategory = nameToCategory.get(parentCategoryName); + if (categoryToNestedCategories.get(parentCategory) == null) { + categoryToNestedCategories.put(parentCategory, new HashSet<SiteCategory>()); + } + categoryToNestedCategories.get(parentCategory).add(category); + } + } + + // sort category so they are processed in reverse order of dependency + // category2 is nested in category1 => category2 < category1 + Comparator<SiteCategory> isNestedCategoryComparator = new Comparator<SiteCategory>() { + public int compare(SiteCategory category1, SiteCategory category2) { + if (Arrays.asList(category1.getCategoryNames()).contains(category2.getName())) { + // category2 is nested in category1 + return -1; + } else if (Arrays.asList(category2.getCategoryNames()).contains(category1.getName())) { + // category1 is nested in category2 + return +1; + } + return 0; + } + }; + List<SiteCategory> categories = new ArrayList<SiteCategory>(Arrays.asList(this.updateSite.getSite().getCategories())); + categories.add(this.defaultCategory); + Collections.sort(categories, isNestedCategoryComparator); + + // Then create categories in the right order + Map<String, IInstallableUnit> nameToCategoryIU = new HashMap<String, IInstallableUnit>(); + for (SiteCategory category : categories) { + Set<IInstallableUnit> units = categoriesToIUs.get(category); + if (units == null) { + units = new HashSet<IInstallableUnit>(); + } + Set<SiteCategory> nestedCategories = categoryToNestedCategories.get(category); + if (nestedCategories != null) { + for (SiteCategory nestedCategory : nestedCategories) { + IInstallableUnit nestedCategoryIU = nameToCategoryIU.get(nestedCategory.getName()); + if (nestedCategoryIU != null) { + units.add(nestedCategoryIU); + } + } + } + if (!units.isEmpty()) { + IInstallableUnit categoryIU = createCategoryIU(category, units); + result.addIU(categoryIU, IPublisherResult.NON_ROOT); + nameToCategoryIU.put(category.getName(), categoryIU); + } } } /** * Creates an IU corresponding to an update site category * @param category The category descriptor - * @param featureIUs The IUs of the features that belong to the category - * @param parentCategory The parent category, or <code>null</code> + * @param childrenIUs The IUs of the children that belong to the category (can be bundle, feature or nested categories) + * @param nestedCategory A nested category (optional) * @return an IU representing the category + * @deprecated use {@link IInstallableUnit}{@link #createCategoryIU(SiteCategory, Set)} instead */ - public IInstallableUnit createCategoryIU(SiteCategory category, Set<IInstallableUnit> featureIUs, IInstallableUnit parentCategory) { + @Deprecated + public IInstallableUnit createCategoryIU(SiteCategory category, Set<IInstallableUnit> childrenIUs, IInstallableUnit nestedCategory) { + Set<IInstallableUnit> allIUs = new HashSet<IInstallableUnit>(); + if (childrenIUs != null) { + allIUs.addAll(childrenIUs); + } + if (nestedCategory != null) { + allIUs.add(nestedCategory); + } + return createCategoryIU(category, allIUs); + } + + /** + * Creates an IU corresponding to an update site category + * @param category The category descriptor + * @param childrenIUs The IUs of the children that belong to the category (can be bundle, feature or nested categories) + * @return an IU representing the category + */ + public IInstallableUnit createCategoryIU(SiteCategory category, Set<IInstallableUnit> childrenIUs) { InstallableUnitDescription cat = new MetadataFactory.InstallableUnitDescription(); cat.setSingleton(true); String categoryId = buildCategoryId(category.getName()); cat.setId(categoryId); if (categoryVersion == null) - cat.setVersion(Version.createOSGi(1, 0, 0, versionSuffixGenerator.generateSuffix(featureIUs, CollectionUtils.<IVersionedId> emptyList()))); + cat.setVersion(Version.createOSGi(1, 0, 0, versionSuffixGenerator.generateSuffix(childrenIUs, CollectionUtils.<IVersionedId> emptyList()))); else { if (categoryVersion.isOSGiCompatible()) { org.osgi.framework.Version osgiVersion = PublisherHelper.toOSGiVersion(categoryVersion); String qualifier = osgiVersion.getQualifier(); if (qualifier.endsWith(QUALIFIER)) { - String suffix = versionSuffixGenerator.generateSuffix(featureIUs, CollectionUtils.<IVersionedId> emptyList()); + String suffix = versionSuffixGenerator.generateSuffix(childrenIUs, CollectionUtils.<IVersionedId> emptyList()); qualifier = qualifier.substring(0, qualifier.length() - 9) + suffix; categoryVersion = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), qualifier); } @@ -457,15 +528,11 @@ public class SiteXMLAction extends AbstractPublisherAction { cat.setProperty(IInstallableUnit.PROP_NAME, label != null ? label : category.getName()); cat.setProperty(IInstallableUnit.PROP_DESCRIPTION, category.getDescription()); - ArrayList<IRequirement> reqsConfigurationUnits = new ArrayList<IRequirement>(featureIUs.size()); - for (IInstallableUnit iu : featureIUs) { + ArrayList<IRequirement> reqsConfigurationUnits = new ArrayList<IRequirement>(childrenIUs.size()); + for (IInstallableUnit iu : childrenIUs) { VersionRange range = new VersionRange(iu.getVersion(), true, iu.getVersion(), true); reqsConfigurationUnits.add(MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), range, iu.getFilter(), false, false)); } - //note that update sites don't currently support nested categories, but it may be useful to add in the future - if (parentCategory != null) { - reqsConfigurationUnits.add(MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, parentCategory.getId(), VersionRange.emptyRange, parentCategory.getFilter(), false, false)); - } cat.setRequirements(reqsConfigurationUnits.toArray(new IRequirement[reqsConfigurationUnits.size()])); // Create set of provided capabilities @@ -492,7 +559,7 @@ public class SiteXMLAction extends AbstractPublisherAction { } /** - * Creates a qualified category id. This action's qualifier is used if one exists + * Creates a qualified category id. This action's qualifier is used if one exists * or an existing update site's location is used. */ private String buildCategoryId(String categoryName) { |