diff options
author | Dave Stevenson | 2007-10-26 21:54:11 +0000 |
---|---|---|
committer | Dave Stevenson | 2007-10-26 21:54:11 +0000 |
commit | 5848dddc54c63006c7b2a208d2d3e0d03433557d (patch) | |
tree | 06301e5cdf7bce73b64c7f092072670274ab0890 /bundles | |
parent | c14f1952ef56c2690f856cf1825071ba7c251cf8 (diff) | |
download | rt.equinox.p2-5848dddc54c63006c7b2a208d2d3e0d03433557d.tar.gz rt.equinox.p2-5848dddc54c63006c7b2a208d2d3e0d03433557d.tar.xz rt.equinox.p2-5848dddc54c63006c7b2a208d2d3e0d03433557d.zip |
Initial delivery of SAX XML Writer/Parser framework for XStream replacement. The metadata and artifact repository writers/parsers are included, but not currently used.
Diffstat (limited to 'bundles')
38 files changed, 3058 insertions, 102 deletions
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/.classpath b/bundles/org.eclipse.equinox.p2.artifact.repository/.classpath index 7cdeb7319..ce7393340 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/.classpath +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/.classpath @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF index df592a14e..daa676b87 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/META-INF/MANIFEST.MF @@ -10,8 +10,10 @@ Export-Package: org.eclipse.equinox.internal.p2.artifact.repository;x-friends:=" org.eclipse.equinox.p2.artifact.repository.processing, org.eclipse.equinox.spi.p2.artifact.repository Import-Package: com.thoughtworks.xstream, - org.eclipse.core.runtime.preferences, + javax.xml.parsers, + org.eclipse.core.runtime.preferences;resolution:=optional, org.eclipse.equinox.internal.p2.jarprocessor;resolution:=optional, + org.eclipse.equinox.internal.p2.metadata, org.eclipse.equinox.p2.core.helpers, org.eclipse.equinox.p2.core.location, org.eclipse.equinox.p2.core.repository, @@ -19,9 +21,12 @@ Import-Package: com.thoughtworks.xstream, org.eclipse.equinox.p2.metadata, org.eclipse.equinox.spi.p2.core.repository, org.eclipse.osgi.internal.provisional.verifier, + org.eclipse.osgi.service.resolver;version="1.1.0", + org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.3.0", org.osgi.service.prefs;version="1.1.0", - org.osgi.util.tracker;version="1.3.0" + org.osgi.util.tracker;version="1.3.0", + org.xml.sax;resolution:=optional Bundle-Activator: org.eclipse.equinox.internal.p2.artifact.repository.Activator Eclipse-LazyStart: true Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1, diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/ArtifactRepositoryIO.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/ArtifactRepositoryIO.java index 0c2208f40..e51af1740 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/ArtifactRepositoryIO.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/ArtifactRepositoryIO.java @@ -12,24 +12,95 @@ package org.eclipse.equinox.internal.p2.artifact.repository; import com.thoughtworks.xstream.XStream; import java.io.*; +import java.util.*; +import javax.xml.parsers.ParserConfigurationException; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.p2.artifact.repository.ArtifactDescriptor; import org.eclipse.equinox.p2.artifact.repository.IArtifactRepository; +import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor; +import org.eclipse.equinox.p2.core.helpers.*; import org.eclipse.equinox.p2.core.repository.RepositoryCreationException; +import org.eclipse.equinox.p2.metadata.IArtifactKey; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Version; +import org.xml.sax.*; /** - * This class reads and writes artifact repository - * (eg. table of contents files); + * This class reads and writes artifact repository metadata + * (e.g. table of contents files); * * This class is not used for reading or writing the actual artifacts. * * The implementation currently uses XStream. */ + +// TODO: this class should be renamed to SimpleArtifactRepositoryIO +// when the use of XStream is eliminated. +// TODO: Should a registration/factory mechanism be supported +// for getting a repository reader/writer given a repository type class ArtifactRepositoryIO { /** + * Writes the given artifact repository to the stream. + * This method performs buffering, and closes the stream when finished. + * + * Persistence implementation using XStream + * @deprecated + */ + public static void write(SimpleArtifactRepository repository, OutputStream output) { + XStream stream = new XStream(); + OutputStream bufferedOutput = null; + try { + try { + bufferedOutput = new BufferedOutputStream(output); + stream.toXML(repository, bufferedOutput); + } finally { + if (bufferedOutput != null) { + bufferedOutput.close(); + } + } + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Write the given SimpleArtifactRrepository to the given stream. + * This method closes the stream when finished. + */ + public void writeNew(SimpleArtifactRepository repository, OutputStream output) { + OutputStream bufferedOutput = null; + try { + try { + bufferedOutput = new BufferedOutputStream(output); + Writer repositoryWriter = new Writer(bufferedOutput); + repositoryWriter.write(repository); + } finally { + if (bufferedOutput != null) { + bufferedOutput.close(); + } + } + } catch (IOException ioe) { + // TODO shouldn't this throw a core exception? + ioe.printStackTrace(); + } + } + + /** * Reads the artifact repository from the given stream, * and returns the contained array of abstract artifact repositories. * * This method performs buffering, and closes the stream when finished. + * + * Persistence implementation using XStream + * @deprecated */ public static IArtifactRepository read(InputStream input) throws RepositoryCreationException { XStream stream = new XStream(); @@ -48,27 +119,439 @@ class ArtifactRepositoryIO { } /** - * Writes the given artifact repository to the stream. - * This method performs buffering, and closes the stream when finished. + * Construct a SimpleArtifactRepository by reading from the given stream. + * This method closes the stream when finished. */ - public static void write(IArtifactRepository repository, OutputStream output) { - XStream stream = new XStream(); - OutputStream bufferedOutput = null; + public SimpleArtifactRepository readNew(InputStream input) throws RepositoryCreationException { + BufferedInputStream bufferedInput = null; try { try { - bufferedOutput = new BufferedOutputStream(output); - stream.toXML(repository, bufferedOutput); + bufferedInput = new BufferedInputStream(input); + Parser repositoryParser = new Parser(Activator.getContext(), Activator.ID); + repositoryParser.parse(input); + if (!repositoryParser.isValidXML()) { + throw new RepositoryCreationException(new CoreException(repositoryParser.getStatus())); + } + return repositoryParser.getRepository(); } finally { - if (bufferedOutput != null) { - bufferedOutput.close(); + if (bufferedInput != null) + bufferedInput.close(); + } + } catch (IOException ioe) { + throw new RepositoryCreationException(ioe); + } + } + + private interface XMLConstants extends org.eclipse.equinox.p2.core.helpers.XMLConstants { + + // Constants defining the structure of the XML for a SimpleArtifactRepository + + // A format version number for simple artifact repository XML. + public static final String XML_VERSION = "0.0.1"; //$NON-NLS-1$ + public static final Version CURRENT_VERSION = new Version(XML_VERSION); + public static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, CURRENT_VERSION, true); + + // Constants for processing instructions + public static final String PI_REPOSITORY_TARGET = "artifactRepository"; //$NON-NLS-1$ + public static XMLWriter.ProcessingInstruction[] PI_DEFAULTS = new XMLWriter.ProcessingInstruction[] {XMLWriter.ProcessingInstruction.makeClassVersionInstruction(PI_REPOSITORY_TARGET, SimpleArtifactRepository.class, CURRENT_VERSION)}; + + // Constants for artifact repository elements + public static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$ + public static final String MAPPING_RULES_ELEMENT = "mappings"; //$NON-NLS-1$ + public static final String MAPPING_RULE_ELEMENT = "rule"; //$NON-NLS-1$ + public static final String ARTIFACTS_ELEMENT = "artifacts"; //$NON-NLS-1$ + public static final String ARTIFACT_ELEMENT = "artifact"; //$NON-NLS-1$ + public static final String PROCESSING_STEPS_ELEMENT = "processing"; //$NON-NLS-1$ + public static final String PROCESSING_STEP_ELEMENT = "step"; //$NON-NLS-1$ + + // Constants for attributes of artifact repository elements + public static final String VERIFY_SIGNATURE_ATTRIBUTE = "verify"; //$NON-NLS-1$ + + public static final String MAPPING_RULE_FILTER_ATTRIBUTE = "filter"; //$NON-NLS-1$ + public static final String MAPPING_RULE_OUTPUT_ATTRIBUTE = "output"; //$NON-NLS-1$ + + public static final String ARTIFACT_NAMESPACE_ATTRIBUTE = NAMESPACE_ATTRIBUTE; + public static final String ARTIFACT_CLASSIFIER_ATTRIBUTE = CLASSIFIER_ATTRIBUTE; + + public static final String STEP_DATA_ATTRIBUTE = "data"; //$NON-NLS-1$ + public static final String STEP_REQUIRED_ATTRIBUTE = "required"; //$NON-NLS-1$ + } + + // XML writer for a SimpleArtifactRepository + protected class Writer extends XMLWriter implements XMLConstants { + + public Writer(OutputStream output) throws IOException { + super(output, PI_DEFAULTS); + } + + /** + * Write the given artifact repository to the output stream. + */ + public void write(SimpleArtifactRepository repository) { + start(REPOSITORY_ELEMENT); + attribute(NAME_ATTRIBUTE, repository.getName()); + attribute(TYPE_ATTRIBUTE, repository.getType()); + attribute(VERSION_ATTRIBUTE, repository.getVersion()); + attributeOptional(PROVIDER_ATTRIBUTE, repository.getProvider()); + attributeOptional(DESCRIPTION_ATTRIBUTE, repository.getDescription()); // TODO: could be cdata? + attribute(VERIFY_SIGNATURE_ATTRIBUTE, repository.getSignatureVerification(), false); + + writeProperties(repository.getProperties()); + writeMappingRules(repository.getRules()); + writeArtifacts(repository.getDescriptors()); + + end(REPOSITORY_ELEMENT); + flush(); + } + + private void writeMappingRules(String[][] rules) { + if (rules.length > 0) { + start(MAPPING_RULES_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, rules.length); + for (int i = 0; i < rules.length; i++) { + start(MAPPING_RULE_ELEMENT); + attribute(MAPPING_RULE_FILTER_ATTRIBUTE, rules[i][0]); + attribute(MAPPING_RULE_OUTPUT_ATTRIBUTE, rules[i][1]); + end(MAPPING_RULE_ELEMENT); + } + end(MAPPING_RULES_ELEMENT); + } + } + + private void writeArtifacts(Set artifactDescriptors) { + start(ARTIFACTS_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, artifactDescriptors.size()); + for (Iterator iter = artifactDescriptors.iterator(); iter.hasNext();) { + ArtifactDescriptor descriptor = (ArtifactDescriptor) iter.next(); + IArtifactKey key = descriptor.getArtifactKey(); + start(ARTIFACT_ELEMENT); + attribute(ARTIFACT_NAMESPACE_ATTRIBUTE, key.getNamespace()); + attribute(ARTIFACT_CLASSIFIER_ATTRIBUTE, key.getClassifier()); + attribute(ID_ATTRIBUTE, key.getId()); + attribute(VERSION_ATTRIBUTE, key.getVersion()); + writeProcessingSteps(descriptor.getProcessingSteps()); + writeProperties(descriptor.getProperties()); + end(ARTIFACT_ELEMENT); + } + end(ARTIFACTS_ELEMENT); + } + + private void writeProcessingSteps(ProcessingStepDescriptor[] processingSteps) { + if (processingSteps.length > 0) { + start(PROCESSING_STEPS_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, processingSteps.length); + for (int i = 0; i < processingSteps.length; i++) { + start(PROCESSING_STEP_ELEMENT); + attribute(ID_ATTRIBUTE, processingSteps[i].getProcessorId()); + attribute(STEP_DATA_ATTRIBUTE, processingSteps[i].getData()); + attribute(STEP_REQUIRED_ATTRIBUTE, processingSteps[i].isRequired()); + end(PROCESSING_STEP_ELEMENT); + } + end(PROCESSING_STEPS_ELEMENT); + } + } + } + + /* + * Parser for the contents of a SimpleArtifactRepository, + * as written by the Writer class. + */ + private class Parser extends XMLParser implements XMLConstants { + + private SimpleArtifactRepository theRepository = null; + + public Parser(BundleContext context, String bundleId) { + super(context, bundleId); + } + + public void parse(File file) throws IOException { + parse(new FileInputStream(file)); + } + + public synchronized void parse(InputStream stream) throws IOException { + this.status = null; + try { + // TODO: currently not caching the parser since we make no assumptions + // or restrictions on concurrent parsing + getParser(); + RepositoryHandler repositoryHandler = new RepositoryHandler(); + xmlReader.setContentHandler(new RepositoryDocHandler(REPOSITORY_ELEMENT, repositoryHandler)); + xmlReader.parse(new InputSource(stream)); + if (isValidXML()) { + theRepository = repositoryHandler.getRepository(); + } + } catch (SAXException e) { + throw new IOException(e.getMessage()); + } catch (ParserConfigurationException e) { + throw new IOException(e.getMessage()); + } finally { + stream.close(); + } + } + + public SimpleArtifactRepository getRepository() { + return theRepository; + } + + protected Object getRootObject() { + return theRepository; + } + + private final class RepositoryDocHandler extends DocHandler { + + public RepositoryDocHandler(String rootName, RootHandler rootHandler) { + super(rootName, rootHandler); + } + + public void ProcessingInstruction(String target, String data) throws SAXException { + if (PI_REPOSITORY_TARGET.equalsIgnoreCase(target)) { + // TODO: should the root handler be constructed based on class + // via an extension registry mechanism? + // String clazz = extractPIClass(data); + // and + // TODO: version tolerance by extension + Version repositoryVersion = extractPIVersion(target, data); + if (!XML_TOLERANCE.isIncluded(repositoryVersion)) { + throw new SAXException(NLS.bind(Messages.SimpleArtifactRepositoryIO_Parser_Has_Incompatible_Version, repositoryVersion, XML_TOLERANCE)); + } + } + } + + } + + private final class RepositoryHandler extends RootHandler { + + private final String[] required = new String[] {NAME_ATTRIBUTE, TYPE_ATTRIBUTE, VERSION_ATTRIBUTE}; + private final String[] optional = new String[] {DESCRIPTION_ATTRIBUTE, PROVIDER_ATTRIBUTE, VERIFY_SIGNATURE_ATTRIBUTE}; + + private String[] attrValues = new String[required.length + optional.length]; + + private MappingRulesHandler mappingRulesHandler = null; + private PropertiesHandler propertiesHandler = null; + private ArtifactsHandler artifactsHandler = null; + + private SimpleArtifactRepository repository = null; + + public RepositoryHandler() { + super(); + } + + public SimpleArtifactRepository getRepository() { + return repository; + } + + protected void handleRootAttributes(Attributes attributes) { + attrValues = parseAttributes(attributes, required, optional); + attrValues[2] = checkVersion(REPOSITORY_ELEMENT, VERSION_ATTRIBUTE, attrValues[2]).toString(); + attrValues[5] = checkBoolean(REPOSITORY_ELEMENT, VERIFY_SIGNATURE_ATTRIBUTE, attrValues[5], false).toString(); + } + + public void startElement(String name, Attributes attributes) { + if (MAPPING_RULES_ELEMENT.equalsIgnoreCase(name)) { + if (mappingRulesHandler == null) { + mappingRulesHandler = new MappingRulesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (ARTIFACTS_ELEMENT.equalsIgnoreCase(name)) { + if (artifactsHandler == null) { + artifactsHandler = new ArtifactsHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (PROPERTIES_ELEMENT.equalsIgnoreCase(name)) { + if (propertiesHandler == null) { + propertiesHandler = new PropertiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else { + invalidElement(name, attributes); + } + } + + protected void finished() { + if (isValidXML()) { + String[][] mappingRules = (mappingRulesHandler == null ? new String[0][0] // + : mappingRulesHandler.getMappingRules()); + OrderedProperties properties = (propertiesHandler == null ? new OrderedProperties(0) // + : propertiesHandler.getProperties()); + Set artifacts = (artifactsHandler == null ? new HashSet(0) // + : artifactsHandler.getArtifacts()); + boolean verifySignature = (attrValues[5] == null ? false // + : new Boolean(attrValues[5]).booleanValue()); + repository = new SimpleArtifactRepository(attrValues[0], attrValues[1], attrValues[2], attrValues[3], // + attrValues[4], verifySignature, artifacts, mappingRules, properties); } } - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); } + + protected class MappingRulesHandler extends AbstractHandler { + + private List mappingRules; + + public MappingRulesHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, MAPPING_RULES_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + mappingRules = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public String[][] getMappingRules() { + String[][] rules = new String[mappingRules.size()][2]; + for (int index = 0; index < mappingRules.size(); index++) { + String[] ruleAttributes = (String[]) mappingRules.get(index); + rules[index] = ruleAttributes; + } + return rules; + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(MAPPING_RULE_ELEMENT)) { + new MappingRuleHandler(this, attributes, mappingRules); + } else { + invalidElement(name, attributes); + } + } + } + + protected class MappingRuleHandler extends AbstractHandler { + + private final String[] required = new String[] {MAPPING_RULE_FILTER_ATTRIBUTE, MAPPING_RULE_OUTPUT_ATTRIBUTE}; + + public MappingRuleHandler(AbstractHandler parentHandler, Attributes attributes, List mappingRules) { + super(parentHandler, MAPPING_RULE_ELEMENT); + mappingRules.add(parseRequiredAttributes(attributes, required)); + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + } + + protected class ArtifactsHandler extends AbstractHandler { + + private Set artifacts; + + public ArtifactsHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, ARTIFACTS_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + artifacts = (size != null ? new LinkedHashSet(new Integer(size).intValue()) : new LinkedHashSet(4)); + } + + public Set getArtifacts() { + return artifacts; + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(ARTIFACT_ELEMENT)) { + new ArtifactHandler(this, attributes, artifacts); + } else { + invalidElement(name, attributes); + } + } + } + + protected class ArtifactHandler extends AbstractHandler { + + private final String[] required = new String[] {ARTIFACT_NAMESPACE_ATTRIBUTE, ARTIFACT_CLASSIFIER_ATTRIBUTE, ID_ATTRIBUTE, VERSION_ATTRIBUTE}; + + private Set artifacts; + ArtifactDescriptor currentArtifact = null; + + private PropertiesHandler propertiesHandler = null; + private ProcessingStepsHandler processingStepsHandler = null; + + public ArtifactHandler(AbstractHandler parentHandler, Attributes attributes, Set artifacts) { + super(parentHandler, ARTIFACT_ELEMENT); + this.artifacts = artifacts; + String[] values = parseRequiredAttributes(attributes, required); + Version version = checkVersion(ARTIFACT_ELEMENT, VERSION_ATTRIBUTE, values[3]); + // TODO: resolve access restriction on ArtifactKey construction + currentArtifact = new ArtifactDescriptor(new ArtifactKey(values[0], values[1], values[2], version)); + } + + public ArtifactDescriptor getArtifact() { + return currentArtifact; + } + + public void startElement(String name, Attributes attributes) { + if (PROCESSING_STEPS_ELEMENT.equalsIgnoreCase(name)) { + if (processingStepsHandler == null) { + processingStepsHandler = new ProcessingStepsHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (PROPERTIES_ELEMENT.equalsIgnoreCase(name)) { + if (propertiesHandler == null) { + propertiesHandler = new PropertiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else { + invalidElement(name, attributes); + } + } + + protected void finished() { + if (isValidXML() && currentArtifact != null) { + OrderedProperties properties = (propertiesHandler == null ? new OrderedProperties(0) // + : propertiesHandler.getProperties()); + currentArtifact.addProperties(properties); + ProcessingStepDescriptor[] processingSteps = (processingStepsHandler == null ? new ProcessingStepDescriptor[0] // + : processingStepsHandler.getProcessingSteps()); + currentArtifact.setProcessingSteps(processingSteps); + artifacts.add(currentArtifact); + } + } + } + + protected class ProcessingStepsHandler extends AbstractHandler { + + private List processingSteps; + + public ProcessingStepsHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, PROCESSING_STEPS_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + processingSteps = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public ProcessingStepDescriptor[] getProcessingSteps() { + return (ProcessingStepDescriptor[]) processingSteps.toArray(new ProcessingStepDescriptor[processingSteps.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(PROCESSING_STEP_ELEMENT)) { + new ProcessingStepHandler(this, attributes, processingSteps); + } else { + invalidElement(name, attributes); + } + } + } + + protected class ProcessingStepHandler extends AbstractHandler { + + private final String[] required = new String[] {ID_ATTRIBUTE, STEP_DATA_ATTRIBUTE, STEP_REQUIRED_ATTRIBUTE}; + + public ProcessingStepHandler(AbstractHandler parentHandler, Attributes attributes, List processingSteps) { + super(parentHandler, PROCESSING_STEP_ELEMENT); + String[] attributeValues = parseRequiredAttributes(attributes, required); + processingSteps.add(new ProcessingStepDescriptor(attributeValues[0], attributeValues[1], checkBoolean(PROCESSING_STEP_ELEMENT, STEP_REQUIRED_ATTRIBUTE, attributeValues[2]).booleanValue())); + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + } + + protected String getErrorMessage() { + return Messages.SimpleArtifactRepositoryIO_Parser_Error_Parsing_Repository; + } + + public String toString() { + // TODO: + return null; + } + } + } diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java new file mode 100644 index 000000000..18c01183e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2007 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.artifact.repository; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.equinox.internal.p2.artifact.repository.messages"; //$NON-NLS-1$ + + static { + // initialize resource bundles + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + // Do not instantiate + } + + public static String SimpleArtifactRepositoryIO_Parser_Error_Parsing_Repository; + public static String SimpleArtifactRepositoryIO_Parser_Has_Incompatible_Version; + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/SimpleArtifactRepository.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/SimpleArtifactRepository.java index 5324f3dfa..bb8c7b0b3 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/SimpleArtifactRepository.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/SimpleArtifactRepository.java @@ -16,6 +16,8 @@ import org.eclipse.core.runtime.*; import org.eclipse.equinox.p2.artifact.repository.*; import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStep; import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepHandler; +import org.eclipse.equinox.p2.core.helpers.MultiStatus; +import org.eclipse.equinox.p2.core.helpers.OrderedProperties; import org.eclipse.equinox.p2.core.repository.IRepository; import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.spi.p2.artifact.repository.AbstractArtifactRepository; @@ -35,7 +37,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme protected String[][] mappingRules = DEFAULT_MAPPING_RULES; protected Set artifactDescriptors = new HashSet(); private boolean signatureVerification = false; - private BlobStore blobStore; + private transient BlobStore blobStore; public static URL getActualLocation(URL base) { String spec = base.toExternalForm(); @@ -71,6 +73,14 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme blobStore = new BlobStore(getBlobStoreLocation(location), 128); } + public SimpleArtifactRepository(String name, String type, String version, String description, String provider, boolean verifySignature, Set artifacts, String[][] mappingRules, OrderedProperties properties) { + super(name, type, version, null, description, provider); + signatureVerification = verifySignature; + this.artifactDescriptors.addAll(artifacts); + this.mappingRules = mappingRules; + this.properties.putAll(properties); + } + private IStatus getArtifact(ArtifactRequest request, IProgressMonitor monitor) { request.setSourceRepository(this); request.perform(monitor); @@ -171,7 +181,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme public File getArtifactFile(IArtifactDescriptor descriptor) { String result = getLocation(descriptor); - if (result == null || !result.startsWith("file:")) + if (result == null || !result.startsWith("file:")) //$NON-NLS-1$ return null; return new File(result.substring(5)); } @@ -308,7 +318,8 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme public void save() { try { - FileOutputStream os = new FileOutputStream(getActualLocation(location).getFile()); + URL actualLocation = getActualLocation(location); + FileOutputStream os = new FileOutputStream(actualLocation.getFile()); ArtifactRepositoryIO.write(this, os); } catch (IOException e) { e.printStackTrace(); @@ -341,7 +352,7 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme // Make sure that the file does not exist and that the parents do File outputFile = new File(file); if (outputFile.exists()) - System.err.println("Artifact repository out of synch. Overwriting " + outputFile.getAbsoluteFile()); + System.err.println("Artifact repository out of synch. Overwriting " + outputFile.getAbsoluteFile()); //$NON-NLS-1$ if (!outputFile.getParentFile().exists() && !outputFile.getParentFile().mkdirs()) // TODO: Log an error, or throw an exception? return null; @@ -434,6 +445,10 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme } } + public boolean getSignatureVerification() { + return signatureVerification; + } + public void setSignatureVerification(boolean value) { signatureVerification = value; } @@ -449,4 +464,5 @@ public class SimpleArtifactRepository extends AbstractArtifactRepository impleme public boolean isModifiable() { return true; } + } diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties new file mode 100644 index 000000000..bc3cf48db --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties @@ -0,0 +1,15 @@ +############################################################################### +# Copyright (c) 2007 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 +############################################################################### + +SimpleArtifactRepositoryIO_Parser_Error_Parsing_Repository=\ + Error parsing simple artifact repository +SimpleArtifactRepositoryIO_Parser_Has_Incompatible_Version=\ + Simple artifact repository has incompatible version {0}; expected {1}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/ArtifactDescriptor.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/ArtifactDescriptor.java index 207848333..167cd5476 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/ArtifactDescriptor.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/ArtifactDescriptor.java @@ -10,8 +10,11 @@ *******************************************************************************/ package org.eclipse.equinox.p2.artifact.repository; -import java.util.*; +import java.util.Arrays; +import java.util.Map; import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor; +import org.eclipse.equinox.p2.core.helpers.OrderedProperties; +import org.eclipse.equinox.p2.core.helpers.UnmodifiableProperties; import org.eclipse.equinox.p2.metadata.IArtifactKey; /** @@ -26,22 +29,21 @@ public class ArtifactDescriptor implements IArtifactDescriptor { // The list of post processing steps that must be applied one the artifact once it // has been downloaded (e.g, unpack, then md5 checksum, then...) protected ProcessingStepDescriptor[] processingSteps = EMPTY_STEPS; - protected Map properties = new HashMap(2); + + protected OrderedProperties properties = new OrderedProperties(); + protected transient IArtifactRepository repository; - //QUESTION: Do we need any description or user readable name + // QUESTION: Do we need any description or user readable name public ArtifactDescriptor(IArtifactDescriptor base) { super(); key = base.getArtifactKey(); processingSteps = base.getProcessingSteps(); + properties.putAll(base.getProperties()); repository = base.getRepository(); - // TODO figure out a better way to copy the properties - setProperty(IArtifactDescriptor.ARTIFACT_MD5, base.getProperty(IArtifactDescriptor.ARTIFACT_MD5)); - setProperty(IArtifactDescriptor.ARTIFACT_SIZE, base.getProperty(IArtifactDescriptor.ARTIFACT_SIZE)); - setProperty(IArtifactDescriptor.DOWNLOAD_MD5, base.getProperty(IArtifactDescriptor.DOWNLOAD_MD5)); - setProperty(IArtifactDescriptor.DOWNLOAD_SIZE, base.getProperty(IArtifactDescriptor.DOWNLOAD_SIZE)); - setProperty(IArtifactDescriptor.FORMAT, base.getProperty(IArtifactDescriptor.FORMAT)); + // TODO this property is hardcoded for the blob store. + // setProperty("artifact.uuid", base.getProperty("artifact.uuid")); } public ArtifactDescriptor(ArtifactDescriptor base) { @@ -61,8 +63,8 @@ public class ArtifactDescriptor implements IArtifactDescriptor { return key; } - public String getProperty(String key) { - return (String) properties.get(key); + public String getProperty(String propertyKey) { + return (String) properties.get(propertyKey); } public void setProperty(String key, String value) { @@ -72,6 +74,18 @@ public class ArtifactDescriptor implements IArtifactDescriptor { properties.put(key, value); } + public void addProperties(OrderedProperties additionalProperties) { + properties.putAll(additionalProperties); + } + + /** + * Returns a read-only collection of the properties of the artifact descriptor. + * @return the properties of this artifact descriptor. + */ + public Map getProperties() { + return new UnmodifiableProperties(properties); + } + public ProcessingStepDescriptor[] getProcessingSteps() { return processingSteps; } @@ -137,4 +151,5 @@ public class ArtifactDescriptor implements IArtifactDescriptor { return "canonical: " + key.toString(); //$NON-NLS-1$ return format + ": " + key.toString(); //$NON-NLS-1$ } + }
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/IArtifactDescriptor.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/IArtifactDescriptor.java index 6c0271059..eb12b8429 100644 --- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/IArtifactDescriptor.java +++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/p2/artifact/repository/IArtifactDescriptor.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.equinox.p2.artifact.repository; +import java.util.Map; import org.eclipse.equinox.p2.artifact.repository.processing.ProcessingStepDescriptor; import org.eclipse.equinox.p2.metadata.IArtifactKey; @@ -30,11 +31,17 @@ public interface IArtifactDescriptor { /** * Return the value of the given property in this descriptor <code>null</code> * is returned if no such property exists - * @param key the proerty key to look for + * @param key the property key to look for * @return the value of the given property or <code>null</code> */ public abstract String getProperty(String key); + /** + * Returns a read-only collection of the properties of the artifact descriptor. + * @return the properties of this artifact descriptor. + */ + public Map getProperties(); + /** * Return the list of processing steps associated with this descriptor. * An empty set of steps implies that this descriptor describes a complete diff --git a/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF index 7d2af04c9..2aad67f79 100644 --- a/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.core/META-INF/MANIFEST.MF @@ -14,7 +14,8 @@ Import-Package: com.thoughtworks.xstream, org.eclipse.osgi.service.resolver;version="1.1.0", org.eclipse.osgi.util;version="1.0.0", org.osgi.framework;version="1.3.0", - org.osgi.util.tracker;version="1.3.3" + org.osgi.util.tracker;version="1.3.3", + org.xml.sax Export-Package: org.eclipse.equinox.internal.p2.core;x-internal:=true, org.eclipse.equinox.p2.core, org.eclipse.equinox.p2.core.eventbus, diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/Messages.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/Messages.java index 2360b0e7a..65f733618 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/Messages.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/Messages.java @@ -24,4 +24,17 @@ public class Messages extends NLS { public static String Util_Invalid_Zip_File_Format; public static String Util_Error_Unzipping; + public static String XMLParser_No_SAX_Parser; + public static String XMLParser_Error_At_Line; + public static String XMLParser_Error_At_Line_Column; + public static String XMLParser_Error_At_Name_Line; + public static String XMLParser_Error_At_Name_Line_Column; + public static String XMLParser_Missing_Required_Attribute; + public static String XMLParser_Unexpected_Attribute; + public static String XMLParser_Illegal_Value_For_Attribute; + public static String XMLParser_Unexpected_Element; + public static String XMLParser_Duplicate_Element; + public static String XMLParser_Unexpected_Character_Data; + public static String XMLParser_Element_Not_Allowed; + } diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatus.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatus.java index 58b44901c..eabca5077 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatus.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatus.java @@ -11,20 +11,20 @@ package org.eclipse.equinox.p2.core.helpers; import java.util.*; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.*; /** * An variation of the MultiStatus. Note that this does not * extend runtime.MultiStatus, instead it extends Status, because * the purpose of this class is to avoid a few shortcomings of * runtime.MultiStatus. This is different as follows: - * - Add only non-ok status as child (this prevents bloat). + * - Add only non-OK status as child (this prevents bloat). * - Children is a list instead of an array; helps when add() * is called more often than getChildren() */ public class MultiStatus extends Status { - public final static String bundleId = "org.eclipse.equinox.p2"; + + public final static String bundleId = "org.eclipse.equinox.p2"; //$NON-NLS-1$ public static final int STATUS_CODE_SUCCESS = 0; // Use ArrayList rather than List so ensureCapacity() is available. @@ -68,21 +68,21 @@ public class MultiStatus extends Status { } /** - * A Multi-Status with an exception. + * A MultiStatus with an exception. */ public MultiStatus(int code, String msg, Throwable exception) { this(code, msg, null, exception); } /** - * A Multi-Status with children. + * A MultiStatus with children. */ public MultiStatus(int code, String msg, IStatus[] nested, Throwable exception) { this(OK, bundleId, code, msg, nested, exception); } /** - * For creation from outside of the default plugin. + * For creation from outside of the default plug-in. */ public MultiStatus(String pluginId, int code, String msg, Throwable exception) { this(OK, pluginId, code, msg, null, exception); @@ -96,7 +96,7 @@ public class MultiStatus extends Status { } /** - * A Multi-Status with everything. + * A MultiStatus with everything. */ public MultiStatus(int severity, String pluginId, int code, String msg, IStatus[] nested, Throwable exception) { super(severity, pluginId, code, msg, exception); @@ -113,7 +113,7 @@ public class MultiStatus extends Status { } /** - * Does this status indicate an error or cancelation. + * Does this status indicate an error or cancellation. */ public boolean isErrorOrCancel() { return matches(ERROR | CANCEL); @@ -164,9 +164,9 @@ public class MultiStatus extends Status { /** * Adds the children of the given status as its own - * chldren. This internally uses add(IStatus). This + * children. This internally uses add(IStatus). This * guards against a null status. - * @param status A multi-status IStatus object whose children + * @param status A MultiStatus IStatus object whose children * are to be added to this children. */ public void addAll(IStatus status) { @@ -181,6 +181,26 @@ public class MultiStatus extends Status { } } + /** + * Merges the given status into this MultiStatus. + * Equivalent to <code>add(status)</code> if the + * given status is not a mMultiStatus. + * Equivalent to <code>addAll(status)</code> if the + * given status is a MultiStatus. + * + * @param status the status to merge into this one + * @see #add(IStatus) + * @see #addAll(IStatus) + */ + public void merge(IStatus status) { + Assert.isLegal(status != null); + if (!status.isMultiStatus()) { + add(status); + } else { + addAll(status); + } + } + /* * @see IStatus#getChildren() */ @@ -201,7 +221,7 @@ public class MultiStatus extends Status { /** * Collapses the children into a flat list. - * If all the children are non-multi-status, + * If all the children are non-MultiStatus, * this is essentially getChildren(). * @return An array of IStatus objects. * @see #getChildren() diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatusUtil.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatusUtil.java index 0e2bb3800..6872493a9 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatusUtil.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/MultiStatusUtil.java @@ -65,8 +65,8 @@ public class MultiStatusUtil { } } return sb.toString(); - } else - return status.getMessage(); + } + return status.getMessage(); } public static List getStatusNodes(IStatus root) { @@ -92,10 +92,8 @@ public class MultiStatusUtil { } private static IStatus newNonMultiStatus(IStatus status) { - if (status.isMultiStatus()) - return new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), status.getException()); - else - return status; + return (status.isMultiStatus() ? new Status(status.getSeverity(), status.getPlugin(), status.getCode(), status.getMessage(), status.getException()) // + : status); } private static void collectStatusNodes(IStatus root, List nodes) { @@ -176,9 +174,7 @@ public class MultiStatusUtil { } private static boolean needsRecoding(IStatus status, IStatusRecoder recoder) { - if (!status.isMultiStatus()) - return recoder.needsRecoding(status); - else { + if (status.isMultiStatus()) { if (recoder.needsRecoding(status)) return true; IStatus[] children = status.getChildren(); @@ -189,6 +185,7 @@ public class MultiStatusUtil { } return false; } + return recoder.needsRecoding(status); } private static IStatus doSingleRecode(IStatus status, IStatusRecoder recoder) { diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/OrderedProperties.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/OrderedProperties.java index 2c8e5b660..7fce86635 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/OrderedProperties.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/OrderedProperties.java @@ -34,6 +34,11 @@ public class OrderedProperties implements Map { super(); } + public OrderedProperties(int size) { + super(); + propertyMap = new LinkedHashMap(size); + } + /** * Set the property value. * <p> @@ -65,9 +70,7 @@ public class OrderedProperties implements Map { } public Collection getPropertyKeysCollection() { - if (propertyMap == null) - return Collections.EMPTY_LIST; - return Collections.unmodifiableCollection(propertyMap.keySet()); + return (propertyMap != null ? Collections.unmodifiableCollection(propertyMap.keySet()) : Collections.EMPTY_LIST); } public String[] getPropertyKeys() { diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLConstants.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLConstants.java new file mode 100644 index 000000000..be698b138 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLConstants.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007 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.core.helpers; + +public interface XMLConstants { + + // Constants used in defining a default processing instruction + // including a class name and a version of the associated XML + // for some category of objects. + // e.g. <?category class='a.b.c.SomeClass' version='1.2.3'?> + // + public static final String PI_CLASS_ATTRIBUTE = "class"; //$NON-NLS-1$ + public static final String PI_VERSION_ATTRIBUTE = "version"; //$NON-NLS-1$ + + // Element and attribute names for a standard property collection. + // e.g. <properties size='1'> + // <property name='some_name' value='some_value'/> + // </properties> + public static final String PROPERTIES_ELEMENT = "properties"; //$NON-NLS-1$ + public static final String PROPERTY_ELEMENT = "property"; //$NON-NLS-1$ + public static final String PROPERTY_NAME_ATTRIBUTE = "name"; //$NON-NLS-1$ + public static final String PROPERTY_VALUE_ATTRIBUTE = "value"; //$NON-NLS-1$ + + // Constants for the names of common general attributes + public static final String ID_ATTRIBUTE = "id"; //$NON-NLS-1$ + public static final String TYPE_ATTRIBUTE = "type"; //$NON-NLS-1$ + public static final String NAME_ATTRIBUTE = "name"; //$NON-NLS-1$ + public static final String VERSION_ATTRIBUTE = "version"; //$NON-NLS-1$ + public static final String VERSION_RANGE_ATTRIBUTE = "range"; //$NON-NLS-1$ + public static final String NAMESPACE_ATTRIBUTE = "namespace"; //$NON-NLS-1$ + public static final String CLASSIFIER_ATTRIBUTE = "classifier"; //$NON-NLS-1$ + public static final String DESCRIPTION_ATTRIBUTE = "description"; //$NON-NLS-1$ + public static final String PROVIDER_ATTRIBUTE = "provider"; //$NON-NLS-1$ + + // A constant for the name of an attribute of a collection or array element + // specifying the size or length + public static final String COLLECTION_SIZE_ATTRIBUTE = "size"; //$NON-NLS-1$ + + // A constant for an empty array of attribute names + public static String[] noAttributes = new String[0]; + +} diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLParser.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLParser.java new file mode 100644 index 000000000..5db1860d6 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLParser.java @@ -0,0 +1,729 @@ +/******************************************************************************* + * Copyright (c) 2007 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.core.helpers; + +import java.util.List; +import java.util.regex.Pattern; +import javax.xml.parsers.*; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Version; +import org.osgi.util.tracker.ServiceTracker; +import org.xml.sax.*; +import org.xml.sax.helpers.DefaultHandler; + +public abstract class XMLParser extends DefaultHandler implements XMLConstants { + + // TODO: support logging + // Get the logger associated with the class. + // protected abstract Logger getLogger(); + + // Get the root object that is being parsed. + protected abstract Object getRootObject(); + + // Get a generic parser error message for inclusion in an error status + protected abstract String getErrorMessage(); + + protected BundleContext context; // parser class bundle context + protected String bundleId; // parser class bundle id + + protected XMLReader xmlReader; // the XML reader for the parser + protected ErrorHandler errorHandler; // the error handler for the parser + + protected MultiStatus status = null; // accumulation of non-fatal errors + protected Locator locator = null; // document locator, if supported by the parser + + private static ServiceTracker xmlTracker = null; + + public XMLParser(BundleContext context, String pluginId) { + super(); + this.context = context; + this.bundleId = pluginId; + } + + /** + * Non-fatal errors accumulated during parsing. + */ + public IStatus getStatus() { + return (status != null ? status : Status.OK_STATUS); + } + + public boolean isValidXML() { + return (status == null || !status.isErrorOrCancel()); + } + + private static SAXParserFactory acquireXMLParsing(BundleContext context) { + if (xmlTracker == null) { + xmlTracker = new ServiceTracker(context, SAXParserFactory.class.getName(), null); + xmlTracker.open(); + } + return (SAXParserFactory) xmlTracker.getService(); + } + + protected static void releaseXMLParsing() { + if (xmlTracker != null) { + xmlTracker.close(); + } + } + + protected SAXParser getParser() throws ParserConfigurationException, SAXException { + SAXParserFactory factory = acquireXMLParsing(this.context); + if (factory == null) { + throw new SAXException(Messages.XMLParser_No_SAX_Parser); + } + factory.setNamespaceAware(true); + factory.setValidating(true); + try { + factory.setFeature("http://xml.org/sax/features/string-interning", true); //$NON-NLS-1$ + } catch (SAXException se) { + // some parsers may not support string interning + } + SAXParser theParser = factory.newSAXParser(); + if (theParser == null) { + throw new SAXException(Messages.XMLParser_No_SAX_Parser); + } + xmlReader = theParser.getXMLReader(); + return theParser; + } + + public static String makeSimpleName(String localName, String qualifiedName) { + if (localName != null && localName.length() > 0) { + return localName; + } + int nameSpaceIndex = qualifiedName.indexOf(":"); //$NON-NLS-1$ + return (nameSpaceIndex == -1 ? qualifiedName : qualifiedName.substring(nameSpaceIndex + 1)); + } + + /** + * Set the document locator for the parser + * + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ + public void setDocumentLocator(Locator docLocator) { + locator = docLocator; + } + + /** + * Abstract base class for content handlers + */ + protected abstract class AbstractHandler extends DefaultHandler { + + protected ContentHandler parentHandler = null; + protected String elementHandled = null; + + protected StringBuffer characters = null; // character data inside an element + + public AbstractHandler() { + // Empty constructor for a root handler + } + + public AbstractHandler(ContentHandler parentHandler) { + this.parentHandler = parentHandler; + xmlReader.setContentHandler(this); + } + + public AbstractHandler(ContentHandler parentHandler, String elementHandled) { + this.parentHandler = parentHandler; + xmlReader.setContentHandler(this); + this.elementHandled = elementHandled; + } + + /** + * Set the document locator for the parser + * + * @see org.xml.sax.ContentHandler#setDocumentLocator + */ + public void setDocumentLocator(Locator docLocator) { + locator = docLocator; + } + + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + finishCharacters(); + String name = makeSimpleName(localName, qName); + trace(name, attributes); + startElement(name, attributes); + } + + public abstract void startElement(String name, Attributes attributes) throws SAXException; + + public void invalidElement(String name, Attributes attributes) { + unexpectedElement(this, name, attributes); + new IgnoringHandler(this); + } + + public void endElement(String namespaceURI, String localName, String qName) { + // TODO: throw a bad state error if makeSimpleName(localName, qName) != elementHandled + finishCharacters(); + finished(); + // Restore the parent content handler + xmlReader.setContentHandler(parentHandler); + } + + /** + * An implementation for startElement when there are no sub-elements + */ + protected void noSubElements(String name, Attributes attributes) { + unexpectedElement(this, name, attributes); + // Create a new handler to ignore subsequent nested elements + new IgnoringHandler(this); + } + + /* + * Save up character data until endElement or nested startElement + * + * @see org.xml.sax.ContentHandler#characters + */ + public void characters(char[] chars, int start, int length) { + if (this.characters == null) { + this.characters = new StringBuffer(); + } + this.characters.append(chars, start, length); + } + + // Consume the characters accumulated in this.characters. + // Called before startElement or endElement + private String finishCharacters() { + // common case -- no characters or only whitespace + if (this.characters == null || this.characters.length() == 0) { + return null; + } + if (allWhiteSpace(this.characters)) { + this.characters.setLength(0); + return null; + } + + // process the characters + try { + String trimmedChars = this.characters.toString().trim(); + if (trimmedChars.length() == 0) { + // this shouldn't happen due to the test for allWhiteSpace above + System.err.println("Unexpected non-whitespace characters: " //$NON-NLS-1$ + + trimmedChars); + return null; + } + processCharacters(trimmedChars); + return trimmedChars; + } finally { + this.characters.setLength(0); + } + } + + // Method to override in the handler of an element with CDATA. + protected void processCharacters(String data) { + if (data.length() > 0) { + unexpectedCharacterData(this, data); + } + } + + private boolean allWhiteSpace(StringBuffer sb) { + int length = sb.length(); + for (int i = 0; i < length; i += 1) { + if (!Character.isWhitespace(sb.charAt(i))) { + return false; + } + } + return true; + } + + /** + * Called when this element and all elements nested into it have been + * handled. + */ + protected void finished() { + // Do nothing by default + } + + /* + * A name used to identify the handler. + */ + public String getName() { + return (elementHandled != null ? elementHandled : "NoName"); //$NON-NLS-1$ + } + + /** + * Parse the attributes of an element with a single required attribute. + */ + protected String parseRequiredAttribute(Attributes attributes, String name) { + return parseRequiredAttributes(attributes, new String[] {name})[0]; + } + + /** + * Parse the attributes of an element with two required attributes. + */ + protected String[] parseRequiredAttributes(Attributes attributes, String name1, String name2) { + return parseRequiredAttributes(attributes, new String[] {name1, name2}); + } + + /** + * Parse the attributes of an element with only required attributes. + */ + protected String[] parseRequiredAttributes(Attributes attributes, String[] required) { + return parseAttributes(attributes, required, noAttributes); + } + + /** + * Parse the attributes of an element with a single optional attribute. + */ + protected String parseOptionalAttribute(Attributes attributes, String name) { + return parseAttributes(attributes, noAttributes, new String[] {name})[0]; + } + + /** + * Parse the attributes of an element, given the list of required and optional ones. + * Return values in same order, null for those not present. + * Report errors for missing required attributes or extra ones. + */ + protected String[] parseAttributes(Attributes attributes, String[] required, String[] optional) { + String[] result = new String[required.length + optional.length]; + for (int i = 0; i < attributes.getLength(); i += 1) { + String name = attributes.getLocalName(i); + String value = attributes.getValue(i).trim(); + int j; + if ((j = indexOf(required, name)) >= 0) { + result[j] = value; + } else if ((j = indexOf(optional, name)) >= 0) { + result[required.length + j] = value; + } else { + unexpectedAttribute(elementHandled, name, value); + } + } + for (int i = 0; i < required.length; i += 1) { + checkRequiredAttribute(elementHandled, required[i], result[i]); + } + return result; + } + + } + + /** + * Handler for an XML document. + * + * Using the inelegant name 'DocHandler' to clearly distinguish + * this class from the deprecated org.xml.sax.DocumentHandler. + */ + protected class DocHandler extends AbstractHandler { + + RootHandler rootHandler; + + public DocHandler(String rootName, RootHandler rootHandler) { + super(null, rootName); + this.rootHandler = rootHandler; + } + + public void ProcessingInstruction(String target, String data) throws SAXException { + // Do nothing by default (except suppress warning) + if (false) { + throw new SAXException(""); //$NON-NLS-1$ + } + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(elementHandled)) { + rootHandler.initialize(this, name, attributes); + xmlReader.setContentHandler(rootHandler); + } else { + this.noSubElements(name, attributes); + } + } + + } + + /** + * Abstract handler for the root element. + */ + protected abstract class RootHandler extends AbstractHandler { + + public RootHandler() { + super(); + } + + public void initialize(DocHandler document, String rootName, Attributes attributes) { + this.parentHandler = document; + this.elementHandled = rootName; + handleRootAttributes(attributes); + } + + protected abstract void handleRootAttributes(Attributes attributes); + + } + + /** + * Handler for an ordered properties collection. + */ + protected class PropertiesHandler extends AbstractHandler { + + private OrderedProperties properties; + + public PropertiesHandler(ContentHandler parentHandler, Attributes attributes) { + super(parentHandler, PROPERTIES_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + properties = (size != null ? new OrderedProperties(new Integer(size).intValue()) : new OrderedProperties()); + } + + public OrderedProperties getProperties() { + return properties; + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(PROPERTY_ELEMENT)) { + new PropertyHandler(this, attributes, properties); + } else { + invalidElement(name, attributes); + } + } + + } + + /** + * Handler for a property in an ordered properties collection. + */ + protected class PropertyHandler extends AbstractHandler { + + public PropertyHandler(ContentHandler parentHandler, Attributes attributes, OrderedProperties properties) { + super(parentHandler, PROPERTY_ELEMENT); + String[] property = parseProperty(attributes); + if (isValidProperty(property)) { + properties.setProperty(property[0], property[1]); + } + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + + private String[] parseProperty(Attributes attributes) { + return parseRequiredAttributes(attributes, PROPERTY_NAME_ATTRIBUTE, PROPERTY_VALUE_ATTRIBUTE); + } + + private boolean isValidProperty(String[] property) { + return (property.length == 2 && property[0] != null && property[1] != null); + } + } + + /** + * Handler for an element with only cdata and no sub-elements. + */ + protected class TextHandler extends AbstractHandler { + + private String text = null; + + private List texts = null; + + // Constructor for a subclass that processes the attributes + public TextHandler(AbstractHandler parent, String elementName) { + super(parent, elementName); + } + + // Constructor for a subclass with no attributes + public TextHandler(AbstractHandler parent, String elementName, Attributes attributes) { + super(parent, elementName); + parseAttributes(attributes, noAttributes, noAttributes); + } + + public TextHandler(AbstractHandler parent, String elementName, Attributes attributes, List texts) { + super(parent, elementName); + parseAttributes(attributes, noAttributes, noAttributes); + this.texts = texts; + } + + public String getText() { + return (text != null ? text : ""); //$NON-NLS-1$ + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + + protected void processCharacters(String data) { + this.text = data; + if (texts != null) { + texts.add(getText()); + } + } + + } + + /** + * Handler for ignoring content. + */ + protected class IgnoringHandler extends AbstractHandler { + + public IgnoringHandler(AbstractHandler parent) { + super(parent); + this.elementHandled = "IgnoringAll"; //$NON-NLS-1$ + } + + public void startElement(String name, Attributes attributes) { + noSubElements(name, attributes); + } + + } + + // Helpers for processing instructions that include a Class and/or a Version. + private static final String PI_CLASS_REGEX = PI_CLASS_ATTRIBUTE + "=['\"]([\\S]*)['\"]$"; //$NON-NLS-1$ + private static final Pattern PI_CLASS_PATTERN = Pattern.compile(PI_CLASS_REGEX); + + private static final String PI_VERSION_REGEX = PI_VERSION_ATTRIBUTE + "=['\"]([\\w\\d][\\w\\d\\.]*)['\"]$"; //$NON-NLS-1$ + private static final Pattern PI_VERSION_PATTERN = Pattern.compile(PI_VERSION_REGEX); + + public String extractPIClass(String data) { + return PI_CLASS_PATTERN.matcher(data).replaceAll("$1"); //$NON-NLS-1$ + } + + public Version extractPIVersion(String target, String data) { + return checkVersion(target, PI_VERSION_ATTRIBUTE, PI_VERSION_PATTERN.matcher(data).replaceAll("$1")); //$NON-NLS-1$ + } + + public class ParserStatus extends Status implements IStatus { + + int lineNumber = 0; + int columnNumber = 0; + + public ParserStatus(int severity, int code, String message) { + super(severity, MultiStatus.bundleId, code, message, null); + } + + public ParserStatus(int severity, int code, String message, int lineNumber, int columnNumber) { + super(severity, MultiStatus.bundleId, code, message, null); + setLineNumber(lineNumber); + setColumnNumber(columnNumber); + } + + public int getColumnNumber() { + return columnNumber; + } + + public void setColumnNumber(int columnNumber) { + this.columnNumber = columnNumber; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + } + + public void error(SAXParseException ex) { + addError(ex); + } + + public void fatalError(SAXParseException ex) throws SAXException { + addError(ex); + throw ex; + } + + protected void addError(SAXParseException ex) { + addError(ex.getMessage()); + } + + protected String getErrorPrefix() { + return null; + } + + protected String getErrorSuffix() { + return null; + } + + // Log an error message and add it to the current status. + // + // TODO: should we have a flag to throw an exception on error, + // so parsing can be aborted? + // TODO: flag to produce warnings instead of errors? etc. + public final void addError(String msg) { + int line = 0; + int column = 0; + String key = msg; + Object[] args = new Object[] {}; + String root = (getRootObject() == null ? "" //$NON-NLS-1$ + : " (" + getRootObject() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ + if (this.locator != null) { + String name = this.locator.getSystemId(); + line = this.locator.getLineNumber(); + column = this.locator.getColumnNumber(); + if (line > 0) { + args = new Object[] {msg, root, name, new Integer(line), new Integer(column)}; + if (column > 0) { + key = (name != null ? Messages.XMLParser_Error_At_Name_Line_Column // + : Messages.XMLParser_Error_At_Line_Column); + } else { + key = (name != null ? Messages.XMLParser_Error_At_Name_Line // + : Messages.XMLParser_Error_At_Line); + } + } + } + String errMsg = NLS.bind(key, args); + String prefix = getErrorPrefix(); + String suffix = getErrorSuffix(); + if (prefix != null) { + errMsg = prefix + errMsg; + } + if (suffix != null) { + errMsg = errMsg + suffix; + } + // TODO: support logging + // getLogger().warning(errMsg); + IStatus currStatus = new ParserStatus(IStatus.ERROR, IStatus.OK, errMsg, line, column); + if (this.status == null) { + this.status = new MultiStatus(bundleId, IStatus.OK, new IStatus[] {currStatus}, getErrorMessage(), null); + } else { + this.status.add(currStatus); + } + } + + public void trace(String element, Attributes attributes) { + // TODO: support logging + // if (!getLogger().isDebugLoggable()) { + // return; + // } + // int indentSize = (this.stateStack != null ? this.stateStack.size() - 1 : 1); + // if (attributes == null) { + // indentSize -= 1; + // } + // char[] indent = new char[2 * indentSize]; + // Arrays.fill(indent, ' '); + // StringBuffer sb = new StringBuffer(); + // sb.append(indent); + // sb.append('<'); + // if (attributes != null) { + // sb.append(element); + // toString(sb, attributes); + // } else { + // sb.append('/').append(element); + // } + // sb.append('>'); + // getLogger().debug(sb.toString()); + } + + private static String toString(Attributes attributes) { + StringBuffer result = new StringBuffer(); + toString(result, attributes); + return result.toString(); + } + + private static void toString(StringBuffer sb, Attributes attributes) { + for (int i = 0; i < attributes.getLength(); i += 1) { + String name = attributes.getLocalName(i); + String value = attributes.getValue(i).trim(); + sb.append(' ').append(name); + sb.append('=').append('"'); + sb.append(value); + sb.append('"'); + } + } + + public void error(String msg) { + addError(msg); + } + + public void checkRequiredAttribute(String element, String name, Object value) { + if (value == null) { + addError(NLS.bind(Messages.XMLParser_Missing_Required_Attribute, element, name)); + } + } + + // Check the format of a required boolean attribute + public Boolean checkBoolean(String element, String attribute, String value) { + try { + return Boolean.valueOf(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + return Boolean.FALSE; + } + } + + // Check the format of an optional boolean attribute + public Boolean checkBoolean(String element, String attribute, String value, boolean defaultValue) { + Boolean result = (defaultValue ? Boolean.TRUE : Boolean.FALSE); + if (value != null) { + try { + return Boolean.valueOf(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } + } + return result; + } + + public Version checkVersion(String element, String attribute, String value) { + try { + return new Version(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } + return Version.emptyVersion; + } + + public VersionRange checkVersionRange(String element, String attribute, String value) { + try { + return new VersionRange(value); + } catch (IllegalArgumentException iae) { + invalidAttributeValue(element, attribute, value); + } + return VersionRange.emptyRange; + } + + public void unexpectedAttribute(String element, String attribute, String value) { + addError(NLS.bind(Messages.XMLParser_Unexpected_Attribute, new Object[] {element, attribute, value})); + } + + public void invalidAttributeValue(String element, String attribute, String value) { + addError(NLS.bind(Messages.XMLParser_Illegal_Value_For_Attribute, new Object[] {attribute, element, value})); + } + + public void unexpectedElement(AbstractHandler handler, String element, Attributes attributes) { + addError(NLS.bind(Messages.XMLParser_Unexpected_Element, new Object[] {handler.getName(), element, toString(attributes)})); + } + + public void duplicateElement(AbstractHandler handler, String element, Attributes attributes) { + addError(NLS.bind(Messages.XMLParser_Duplicate_Element, new Object[] {handler.getName(), element, toString(attributes)})); + } + + public void unexpectedCharacterData(AbstractHandler handler, String cdata) { + addError(NLS.bind(Messages.XMLParser_Unexpected_Character_Data, handler.getName(), cdata.trim())); + } + + /** + * Find the index of the first occurrence of object in array, or -1. + * Use Arrays.binarySearch if array is big and sorted. + */ + protected static int indexOf(String[] array, String value) { + for (int i = 0; i < array.length; i += 1) { + if (value == null ? array[i] == null : value.equals(array[i])) { + return i; + } + } + return -1; + } + + // public class BadStateError extends AssertionError { + // private static final long serialVersionUID = 1L; // not serialized + // + // public BadStateError() { + // super("unexpected state" + //$NON-NLS-1$ + // (XMLParser.this.stateStack != null ? ": " + XMLParser.this.stateStack //$NON-NLS-1$ + // : "")); //$NON-NLS-1$ + // } + // + // public BadStateError(String element) { + // super("unexpected state for " + element + //$NON-NLS-1$ + // (XMLParser.this.stateStack != null ? ": " + XMLParser.this.stateStack //$NON-NLS-1$ + // : "")); //$NON-NLS-1$ + // } + // } + +} diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLUtils.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLUtils.java new file mode 100644 index 000000000..ed72bf65c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLUtils.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2007 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.core.helpers; + +public class XMLUtils { + + public static String escape(String txt) { + // Traverse the string from right to left as the length + // may increase as result of processing + for (int i = txt.length() - 1; i >= 0; i -= 1) { + String replace; + switch (txt.charAt(i)) { + case '<' : + replace = "<"; //$NON-NLS-1$ + break; + case '>' : + replace = ">"; //$NON-NLS-1$ + break; + case '"' : + replace = """; //$NON-NLS-1$ + break; + case '\'' : + replace = "'"; //$NON-NLS-1$ + break; + case '&' : + replace = "&"; //$NON-NLS-1$ + break; + default : + continue; + } + txt = txt.substring(0, i) + replace + txt.substring(i + 1); + } + return txt; + } + + // A string constant for no indentation. + protected static final String NO_INDENT = ""; //$NON-NLS-1$ + + // A constant for maximum line length before break and indent + protected static final int MAX_LINE = 72; + + /* + * Construct a string for a simple XML element + * with no text or child elements, just attributes + * expressed as name/value pairs. + * + * e.g <elemName attr1='value1' attr2='value2'/> + */ + public static String simpleElement(String elementName, String[] attrNameValuePairs) { + StringBuffer buffer = new StringBuffer(); + simpleElement(elementName, attrNameValuePairs, buffer, NO_INDENT); + + return buffer.toString(); + } + + /* + * Write a string for a simple XML element + * with no text or child elements, just attributes + * expressed as name/value pairs, into the given + * string buffer using the given indentation. + * + * e.g. <elemName attr1='value1' attr2='value2'/> + */ + public static void simpleElement(String elementName, String[] attrNameValuePairs, StringBuffer buffer, String indent) { + element(elementName, attrNameValuePairs, buffer, indent, "/>"); //$NON-NLS-1$ + } + + /* + * Write a string for an open XML element, including + * the attributes expressed as name/value pairs, + * into the given string buffer using the given indentation. + * + * The caller is responsible for adding any child elements + * and closing the element. + * + * Eg. <elemName attr1='value1' attr2='value2'> + */ + public static void openElement(String elementName, String[] attrNameValuePairs, StringBuffer buffer, String indent) { + element(elementName, attrNameValuePairs, buffer, indent, ">"); //$NON-NLS-1$ + } + + /* + * Write a string for an unterminated XML element, including + * the attributes expressed as name/value pairs, + * into the given string buffer using the given indentation. + * + * The caller is responsible for terminating the element. + * + * e.g. <elemName attr1='value1' attr2='value2' + */ + public static void unterminatedElement(String elementName, String[] attrNameValuePairs, StringBuffer buffer, String indent) { + buffer.append(indent).append('<').append(elementName); + + String attrPrefix = " "; //$NON-NLS-1$ + int totalLen = buffer.length() + 1; + for (int i = 0; i < attrNameValuePairs.length; i++) { + String next = attrNameValuePairs[i]; + if (next != null) { + totalLen += next.length() + 2; + if (totalLen >= MAX_LINE) { + attrPrefix = '\n' + indent + " "; //$NON-NLS-1$ + break; + } + } + } + + for (int i = 0; i < attrNameValuePairs.length;) { + String name = attrNameValuePairs[i++]; + if (name.length() > 0) { + String value = (i < attrNameValuePairs.length ? attrNameValuePairs[i++] : null); + if (value != null) { + buffer.append(attrPrefix).append(name); + buffer.append("='").append(escape(value)).append('\''); //$NON-NLS-1$ + } + } + } + } + + /* + * Fill the string buffer for a XML element, including + * the attributes expressed as name/value pairs, and + * terminate with the given termination string. + * + * e.g. <elemName attr1='value1' attr2='value2'> + * or <elemName attr1='value1' attr2='value2'> + */ + private static void element(String elementName, String[] attrNameValuePairs, StringBuffer buffer, String indent, String termination) { + unterminatedElement(elementName, attrNameValuePairs, buffer, indent); + buffer.append(termination); + } + + private XMLUtils() { + // Private constructor to ensure no instances are created. + } + +} diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLWriter.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLWriter.java new file mode 100644 index 000000000..e5a1fc47d --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/XMLWriter.java @@ -0,0 +1,348 @@ +/******************************************************************************* + * Copyright (c) 2007 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.core.helpers; + +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; +import org.osgi.framework.Version; + +public class XMLWriter implements XMLConstants { + + // Miscellaneous + public static final String EMPTY_ATTR = ""; //$NON-NLS-1$ + + public static class ProcessingInstruction { + + private String target; + private String[] data; + + // The standard UTF-8 processing instruction + public static final String XML_UTF8 = "<?xml version='1.0' encoding='UTF-8'?>"; //$NON-NLS-1$ + + public ProcessingInstruction(String target, String data) { + this.target = target; + this.data = new String[] {data}; + } + + public ProcessingInstruction(String target) { + this.target = target; + this.data = new String[0]; + } + + public ProcessingInstruction(String target, String[] data) { + this.target = target; + this.data = data; + } + + public ProcessingInstruction(String target, String[] attrs, String[] values) { + // Lengths of attributes and values must be the same + this.target = target; + this.data = new String[attrs.length]; + for (int i = 0; i < attrs.length; i++) { + data[i] = attributeImage(attrs[i], values[i]); + } + } + + public static ProcessingInstruction makeClassVersionInstruction(String target, Class clazz, Version version) { + return new ProcessingInstruction(target, new String[] {PI_CLASS_ATTRIBUTE, PI_VERSION_ATTRIBUTE}, new String[] {clazz.getName(), version.toString()}); + } + + public String toString() { + StringBuffer sb = new StringBuffer("<?"); //$NON-NLS-1$ + sb.append(this.target).append(' '); + for (int i = 0; i < data.length; i++) { + sb.append(this.data[i]); + if (i < data.length - 1) { + sb.append(' '); + } + } + sb.append("?>"); //$NON-NLS-1$ + return sb.toString(); + } + } + + private Stack elements; // XML elements that have not yet been closed + private boolean open; // Can attributes be added to the current element? + private String indent; // used for each level of indentation + + private PrintWriter pw; + + private XMLWriter(OutputStream output, boolean writeXMLProcessingInstruction) throws UnsupportedEncodingException { + this.pw = new PrintWriter(new OutputStreamWriter(output, "UTF8"), true); //$NON-NLS-1$ + if (writeXMLProcessingInstruction) { + println(ProcessingInstruction.XML_UTF8); + } + this.elements = new Stack(); + this.open = false; + this.indent = " "; //$NON-NLS-1$ + } + + public XMLWriter(OutputStream output, ProcessingInstruction piElement, boolean writeXMLProcessingInstruction) throws UnsupportedEncodingException { + this(output, new ProcessingInstruction[] {piElement}, writeXMLProcessingInstruction); + } + + public XMLWriter(OutputStream output, ProcessingInstruction piElement) throws UnsupportedEncodingException { + this(output, new ProcessingInstruction[] {piElement}, true /* writeXMLProcessingInstruction */); + } + + public XMLWriter(OutputStream output, ProcessingInstruction[] piElements, boolean writeXMLProcessingInstruction) throws UnsupportedEncodingException { + this(output, writeXMLProcessingInstruction); + if (piElements != null) { + for (int i = 0; i < piElements.length; i++) { + println(piElements[i].toString()); + } + } + } + + public XMLWriter(OutputStream output, ProcessingInstruction[] piElements) throws UnsupportedEncodingException { + this(output, piElements, /* writeXMLProcessingInstruction */ + true); + } + + // String used for each level of indentation; default is two spaces. + public void setIndent(String indent) { + this.indent = indent; + } + + // start a new element + public void start(String name) { + if (this.open) { + println('>'); + } + indent(); + print('<'); + print(name); + this.elements.push(name); + this.open = true; + } + + // end the most recent element with this name + public void end(String name) { + if (this.elements.empty()) { + throw new EndWithoutStartError(); + } + int index = this.elements.search(name); + if (index == -1) { + throw new EndWithoutStartError(name); + } + for (int i = 0; i < index; i += 1) { + end(); + } + } + + // end the current element + public void end() { + if (this.elements.empty()) { + throw new EndWithoutStartError(); + } + String name = (String) this.elements.pop(); + if (this.open) { + println("/>"); //$NON-NLS-1$ + } else { + printlnIndented("</" + name + '>', false); //$NON-NLS-1$ + } + this.open = false; + } + + // write a boolean attribute if it doesn't have the default value + public void attribute(String name, boolean value, boolean defaultValue) { + if (value != defaultValue) { + attribute(name, value); + } + } + + public void attribute(String name, boolean value) { + attribute(name, Boolean.toString(value)); + } + + public void attribute(String name, int value) { + attribute(name, Integer.toString(value)); + } + + public void attributeOptional(String name, String value) { + if (value.length() > 0) { + attribute(name, value); + } + } + + public void attribute(String name, Object value) { + if (!this.open) { + throw new AttributeAfterNestedContentError(); + } + if (value == null) { + return; // optional attribute with no value + } + print(' '); + print(name); + print("='"); //$NON-NLS-1$ + print(XMLUtils.escape(value.toString())); + print('\''); + } + + public void cdata(String data) { + cdata(data, true); + } + + public void cdata(String data, boolean escape) { + if (this.open) { + println('>'); + this.open = false; + } + if (data != null) { + printlnIndented(data, escape); + } + } + + public void cdataLines(String data, boolean escape) { + if (this.open) { + println('>'); + this.open = false; + } + if (data.indexOf('\n') == -1) { + // simple case: one line + printlnIndented(data, escape); + } else { + String[] lines = Pattern.compile("\\s*\\n").split(data, 0); //$NON-NLS-1$ + for (int i = 0; i < lines.length; i += 1) { + printlnIndented(lines[i].trim(), escape); + } + } + } + + public void flush() { + while (!this.elements.empty()) { + try { + end(); + } catch (EndWithoutStartError e) { + // can't happen + } + } + this.pw.flush(); + } + + public void close() { + flush(); + this.pw.close(); + } + + public void writeProperties(Map properties) { + writeProperties(PROPERTIES_ELEMENT, properties); + } + + public void writeProperties(String propertiesElement, Map properties) { + if (properties != null && properties.size() > 0) { + start(propertiesElement); + attribute(COLLECTION_SIZE_ATTRIBUTE, properties.size()); + for (Iterator iter = properties.keySet().iterator(); iter.hasNext();) { + String name = (String) iter.next(); + writeProperty(name, (String) properties.get(name)); + } + end(propertiesElement); + } + } + + // Support writing properties with an auxiliary map + // of keys to localized values + public void writeProperties(Map properties, Map localizedValues) { + writeProperties(PROPERTIES_ELEMENT, properties, localizedValues); + } + + // Support writing properties with an auxiliary map + // of keys to localized values + public void writeProperties(String propertiesElement, Map properties, Map localizedValues) { + if (properties != null && properties.size() > 0 && localizedValues != null) { + start(propertiesElement); + for (Iterator I = properties.entrySet().iterator(); I.hasNext();) { + Map.Entry property = (Map.Entry) I.next(); + String key = (String) property.getKey(); + String val = (String) property.getValue(); + if (localizedValues.containsKey(key)) { + val = (String) localizedValues.get(key); + } + writeProperty(key, val); + } + end(propertiesElement); + } + } + + public void writeProperty(String name, String value) { + start(PROPERTY_ELEMENT); + attribute(PROPERTY_NAME_ATTRIBUTE, name); + attribute(PROPERTY_VALUE_ATTRIBUTE, value); + end(); + } + + protected static String attributeImage(String name, String value) { + if (value == null) { + return EMPTY_ATTR; // optional attribute with no value + } + return name + "='" + XMLUtils.escape(value) + '\''; //$NON-NLS-1$ + } + + private void println(char c) { + this.pw.println(c); + } + + private void println(String s) { + this.pw.println(s); + } + + private void println() { + this.pw.println(); + } + + private void print(char c) { + this.pw.print(c); + } + + private void print(String s) { + this.pw.print(s); + } + + private void printlnIndented(String s, boolean escape) { + if (s.length() == 0) { + println(); + } else { + indent(); + println(escape ? XMLUtils.escape(s) : s); + } + } + + private void indent() { + for (int i = this.elements.size(); i > 0; i -= 1) { + print(this.indent); + } + } + + public static class AttributeAfterNestedContentError extends Error { + private static final long serialVersionUID = 1L; // not serialized + } + + public static class EndWithoutStartError extends Error { + private static final long serialVersionUID = 1L; // not serialized + private String name; + + public EndWithoutStartError() { + super(); + } + + public EndWithoutStartError(String name) { + super(); + this.name = name; + } + + public String getName() { + return this.name; + } + } + +} diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/messages.properties b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/messages.properties index a5294426c..0f58a17af 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/messages.properties +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/p2/core/helpers/messages.properties @@ -12,4 +12,17 @@ Util_Invalid_Zip_File_Format=Invalid zip file format Util_Error_Unzipping=Error unzipping {0}: {1} +# All these errors are bound to 5 args: 0-msg, 1-root, 2-name, 3-line, 4-column +XMLParser_Error_At_Line=Error at line {3}{1}: {0} +XMLParser_Error_At_Line_Column=Error at line {3}, column {4}{1}: {0} +XMLParser_Error_At_Name_Line=Error in {2} at line {3}: {0} +XMLParser_Error_At_Name_Line_Column=Error in {2} at line {3}, column {4}: {0} +XMLParser_No_SAX_Parser=Unable to acquire a SAX parser service. +XMLParser_Missing_Required_Attribute=Missing required attribute in "{0}": {1} +XMLParser_Unexpected_Attribute=Unexpected attribute for element "{0}": {1}="{2}" +XMLParser_Illegal_Value_For_Attribute=Illegal value for attribute "{0}" of element "{1}": {2} +XMLParser_Unexpected_Element=Unexpected element in element "{0}": <{1}{2}> +XMLParser_Duplicate_Element=Duplicate singleton element in element "{0}": <{1}{2}> +XMLParser_Unexpected_Character_Data=Unexpected character data in element "{0}": {1} +XMLParser_Element_Not_Allowed=Element "{0}" is not allowed within element "{1}" diff --git a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/spi/p2/core/repository/AbstractRepository.java b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/spi/p2/core/repository/AbstractRepository.java index 153abc62d..0f9a9b616 100644 --- a/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/spi/p2/core/repository/AbstractRepository.java +++ b/bundles/org.eclipse.equinox.p2.core/src/org/eclipse/equinox/spi/p2/core/repository/AbstractRepository.java @@ -31,7 +31,6 @@ public abstract class AbstractRepository extends PlatformObject implements IRepo protected String version; protected String description; protected String provider; - // TODO make sure that this is transiaent. NO point in storing the location in the repo itself. protected transient URL location; protected OrderedProperties properties = new OrderedProperties(); diff --git a/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT bad.launch b/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT bad.launch new file mode 100644 index 000000000..c2acdb4f9 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT bad.launch @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher"> +<booleanAttribute key="automaticAdd" value="false"/> +<booleanAttribute key="automaticValidate" value="false"/> +<stringAttribute key="bootstrap" value=""/> +<stringAttribute key="checked" value="[NONE]"/> +<booleanAttribute key="clearConfig" value="false"/> +<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/Metadata Generator CDT"/> +<booleanAttribute key="default_auto_start" value="false"/> +<intAttribute key="default_start_level" value="4"/> +<booleanAttribute key="includeOptional" value="false"/> +<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-console -consolelog -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator -metadataRepository file:C:/Ap2/servers/CDTmetadataRepository/ -artifactRepository file:C:/Ap2/servers/CDTartifactRepository/ -source C:/eclipse/CDT4.0 -root cdt -rootVersion 4.0.0 -flavor tooling -publishArtifacts -append"/> +<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/> +<stringAttribute key="target_bundles" value="org.eclipse.core.contenttype@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.runtime@default:default,org.eclipse.core.runtime.compatibility.registry@default:default,org.eclipse.equinox.app@default:true,org.eclipse.equinox.common@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.launcher.win32.win32.x86@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi@:,org.eclipse.osgi.services@default:default,org.eclipse.update.configurator@default:default"/> +<booleanAttribute key="tracing" value="false"/> +<booleanAttribute key="useDefaultConfigArea" value="true"/> +<stringAttribute key="vminstall" value="JRE1.4.2"/> +<stringAttribute key="workspace_bundles" value="com.thoughtworks.xstream@default:default,org.eclipse.ecf@default:default,org.eclipse.ecf.filetransfer@default:default,org.eclipse.ecf.identity@default:default,org.eclipse.equinox.frameworkadmin@default:default,org.eclipse.equinox.frameworkadmin.equinox@default:true,org.eclipse.equinox.p2.artifact.repository@default:default,org.eclipse.equinox.p2.core@default:default,org.eclipse.equinox.p2.download@default:default,org.eclipse.equinox.p2.engine@default:default,org.eclipse.equinox.p2.metadata@default:default,org.eclipse.equinox.p2.metadata.generator@default:default,org.eclipse.equinox.p2.metadata.repository@default:default"/> +</launchConfiguration> diff --git a/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT.launch b/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT.launch index 903a0f163..6ec5e0623 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT.launch +++ b/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator CDT.launch @@ -1,21 +1,22 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher"> +<booleanAttribute key="append.args" value="true"/> <booleanAttribute key="automaticAdd" value="false"/> <booleanAttribute key="automaticValidate" value="false"/> <stringAttribute key="bootstrap" value=""/> <stringAttribute key="checked" value="[NONE]"/> -<booleanAttribute key="clearConfig" value="true"/> -<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/Metadata Generator CDT"/> +<booleanAttribute key="clearConfig" value="false"/> +<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/Metadata Generator CDT2"/> <booleanAttribute key="default_auto_start" value="false"/> <intAttribute key="default_start_level" value="4"/> -<booleanAttribute key="includeOptional" value="true"/> +<booleanAttribute key="includeOptional" value="false"/> <booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> -<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-console -consolelog -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator -metadataRepository file:c:/temp/equinox.p2/servers/CDTmetadataRepository/ -artifactRepository file:c:/temp/equinox.p2/servers/CDTartifactRepository/ -source c:/eclipse/CDT4.0 -root cdt -flavor tooling -publishArtifacts"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-console -consolelog -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator -metadataRepository file:C:/Ap2/servers/CDTmetadataRepository/ -artifactRepository file:C:/Ap2/servers/CDTartifactRepository/ -source C:/eclipse/CDT4.0 -root cdt -rootVersion 4.0.0 -flavor tooling -publishArtifacts -append"/> <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/> -<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Declipse.p2.data.area=c:/temp/equinox.p2/agent/ -Declipse.p2.metadataRepository=file:c:/temp/equinox.p2/servers/CDTmetadataRepository/ -Declipse.p2.artifactRepository=file:c:/temp/equinox.p2/servers/artifactRepository/ -Declipse.ignoreApp=false -Declipse.application.registerDescriptors=true -Declipse.application=org.eclipse.equinox.p2.metadata.generator.EclipseGenerator -Declipse.p2.metadataGenerator.eclipseBase=c:/eclipse/CDT4.0/ -Declipse.p2.rootIUId=cdt -Declipse.p2.configurationFlavor=tooling -Declipse.p2.appendToRepo=true -Declipse.p2.generateArtifacts=true"/> -<stringAttribute key="target_bundles" value="org.eclipse.core.jobs@default:default,org.eclipse.equinox.app@default:true,org.eclipse.equinox.common@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi@:,org.eclipse.osgi.services@default:default"/> +<stringAttribute key="pde.version" value="3.3"/> +<stringAttribute key="target_bundles" value="org.eclipse.core.contenttype@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.runtime@default:default,org.eclipse.core.runtime.compatibility.registry@default:default,org.eclipse.equinox.app@default:true,org.eclipse.equinox.common@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.launcher.win32.win32.x86@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi@:,org.eclipse.osgi.services@default:default,org.eclipse.update.configurator@default:default"/> <booleanAttribute key="tracing" value="false"/> <booleanAttribute key="useDefaultConfigArea" value="true"/> -<stringAttribute key="vminstall" value="jdk"/> +<stringAttribute key="vminstall" value="JRE1.4.2"/> <stringAttribute key="workspace_bundles" value="com.thoughtworks.xstream@default:default,org.eclipse.ecf@default:default,org.eclipse.ecf.filetransfer@default:default,org.eclipse.ecf.identity@default:default,org.eclipse.equinox.frameworkadmin@default:default,org.eclipse.equinox.frameworkadmin.equinox@default:true,org.eclipse.equinox.p2.artifact.repository@default:default,org.eclipse.equinox.p2.core@default:default,org.eclipse.equinox.p2.download@default:default,org.eclipse.equinox.p2.engine@default:default,org.eclipse.equinox.p2.metadata@default:default,org.eclipse.equinox.p2.metadata.generator@default:default,org.eclipse.equinox.p2.metadata.repository@default:default"/> </launchConfiguration> diff --git a/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator SDK.launch b/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator SDK.launch index 0a6ef35fc..c5ec056d1 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator SDK.launch +++ b/bundles/org.eclipse.equinox.p2.metadata.generator/Metadata Generator SDK.launch @@ -11,12 +11,12 @@ <intAttribute key="default_start_level" value="4"/> <booleanAttribute key="includeOptional" value="false"/> <booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> -<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-console -consolelog -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator -metadataRepository file:d:/tmp/equinox.p2/servers/metadataRepository/ -artifactRepository file:d:/tmp/equinox.p2/servers/artifactRepository/ -source d:/3.3/eclipse -root sdk -rootVersion 3.3.0 -flavor tooling -publishArtifacts -append"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-console -consolelog -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator -metadataRepository file:C:/Ap2/servers2/metadataRepository/ -artifactRepository file:C:/Ap2/servers2/artifactRepository/ -source C:/eclipse/3.3/eclipse -root sdk -rootVersion 3.3.0 -flavor tooling -publishArtifacts -append"/> <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/> <stringAttribute key="pde.version" value="3.3"/> -<stringAttribute key="target_bundles" value="org.eclipse.core.contenttype@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.runtime@default:default,org.eclipse.core.runtime.compatibility.registry@default:default,org.eclipse.equinox.app@default:true,org.eclipse.equinox.common@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.launcher.win32.win32.x86@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi@:,org.eclipse.osgi.services@default:default,org.eclipse.update.configurator@default:default"/> +<stringAttribute key="target_bundles" value="com.ibm.icu@default:default,org.eclipse.core.contenttype@default:default,org.eclipse.core.jobs@default:default,org.eclipse.core.runtime@default:default,org.eclipse.core.runtime.compatibility.registry@default:default,org.eclipse.equinox.app@default:true,org.eclipse.equinox.common@default:default,org.eclipse.equinox.launcher@default:default,org.eclipse.equinox.launcher.win32.win32.x86@default:default,org.eclipse.equinox.preferences@default:default,org.eclipse.equinox.registry@default:default,org.eclipse.osgi@:,org.eclipse.osgi.services@default:default,org.eclipse.update.configurator@default:default"/> <booleanAttribute key="tracing" value="false"/> <booleanAttribute key="useDefaultConfigArea" value="true"/> -<stringAttribute key="vminstall" value="jre1.6.0"/> -<stringAttribute key="workspace_bundles" value="com.thoughtworks.xstream@default:default,org.eclipse.ecf@default:default,org.eclipse.ecf.filetransfer@default:default,org.eclipse.ecf.identity@default:default,org.eclipse.equinox.frameworkadmin@default:default,org.eclipse.equinox.frameworkadmin.equinox@default:true,org.eclipse.equinox.p2.artifact.repository@default:default,org.eclipse.equinox.p2.core@default:default,org.eclipse.equinox.p2.download@default:default,org.eclipse.equinox.p2.engine@default:default,org.eclipse.equinox.p2.metadata@default:default,org.eclipse.equinox.p2.metadata.generator@default:default,org.eclipse.equinox.p2.metadata.repository@default:default"/> +<stringAttribute key="vminstall" value="JRE1.4.2"/> +<stringAttribute key="workspace_bundles" value="com.thoughtworks.xstream@default:default,org.apache.xerces@default:default,org.apache.xml.resolver@default:default,org.eclipse.ecf@default:default,org.eclipse.ecf.filetransfer@default:default,org.eclipse.ecf.identity@default:default,org.eclipse.equinox.frameworkadmin@default:default,org.eclipse.equinox.frameworkadmin.equinox@default:true,org.eclipse.equinox.p2.artifact.repository@default:default,org.eclipse.equinox.p2.core@default:default,org.eclipse.equinox.p2.download@default:default,org.eclipse.equinox.p2.engine@default:default,org.eclipse.equinox.p2.metadata@default:default,org.eclipse.equinox.p2.metadata.generator@default:default,org.eclipse.equinox.p2.metadata.repository@default:default"/> </launchConfiguration> diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/.classpath b/bundles/org.eclipse.equinox.p2.metadata.repository/.classpath index 7cdeb7319..ce7393340 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/.classpath +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/.classpath @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/> <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.metadata.repository/META-INF/MANIFEST.MF index 9d141f2ae..218d5a1bc 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/META-INF/MANIFEST.MF @@ -10,6 +10,7 @@ Export-Package: org.eclipse.equinox.internal.p2.metadata.repository;x-friends:=" org.eclipse.equinox.spi.p2.metadata.repository Import-Package: com.thoughtworks.xstream, org.eclipse.core.runtime.preferences, + org.eclipse.equinox.internal.p2.metadata, org.eclipse.equinox.p2.core.eventbus, org.eclipse.equinox.p2.core.helpers, org.eclipse.equinox.p2.core.location, @@ -22,7 +23,8 @@ Import-Package: com.thoughtworks.xstream, org.eclipse.osgi.service.resolver;version="1.1.0", org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.3.0", - org.osgi.service.prefs;version="1.1.0" + org.osgi.service.prefs;version="1.1.0", + org.xml.sax Bundle-Activator: org.eclipse.equinox.internal.p2.metadata.repository.Activator Eclipse-LazyStart: true Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1, diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java index 5a9ebaf4d..1405dcc75 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/LocalMetadataRepository.java @@ -35,7 +35,7 @@ public class LocalMetadataRepository extends AbstractMetadataRepository { static final private Integer REPOSITORY_VERSION = new Integer(1); static final private String CONTENT_FILENAME = "content.xml"; //$NON-NLS-1$ - protected HashSet units = new HashSet(); + protected HashSet units = new LinkedHashSet(); public static File getActualLocation(URL location) { String spec = location.getFile(); @@ -48,12 +48,20 @@ public class LocalMetadataRepository extends AbstractMetadataRepository { return new File(spec); } + public LocalMetadataRepository() { + super(); + } + public LocalMetadataRepository(URL location, String name) throws RepositoryCreationException { super(name == null ? (location != null ? location.toExternalForm() : "") : name, REPOSITORY_TYPE, REPOSITORY_VERSION.toString(), location, null, null); if (!location.getProtocol().equals("file")) //$NON-NLS-1$ throw new IllegalArgumentException("Invalid local repository location: " + location); } + protected LocalMetadataRepository(String name, String type, String version, URL location, String description, String provider) { + super(name, type, version, location, description, provider); + } + public IInstallableUnit[] getInstallableUnits(IProgressMonitor monitor) { if (monitor == null) { monitor = new NullProgressMonitor(); @@ -124,4 +132,21 @@ public class LocalMetadataRepository extends AbstractMetadataRepository { public boolean isModifiable() { return true; } + + // Get a non-modifiable collection of the installable units + // from the repository. + public Set getInstallableUnits() { + return Collections.unmodifiableSet(units); + } + + public void initialize(RepositoryState state) { + this.name = state.Name; + this.type = state.Type; + this.version = state.Version.toString(); + this.provider = state.Provider; + this.description = state.Description; + this.location = state.Location; + this.properties = state.Properties; + this.units.addAll(Arrays.asList(state.Units)); + } } diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/Messages.java b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/Messages.java index 7d2647a9b..942db82c3 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/Messages.java +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/Messages.java @@ -24,4 +24,8 @@ public class Messages extends NLS { public static String REPO_LOADING; public static String REPOMGR_ADDING_REPO; + + public static String MetadataRepositoryIO_Parser_Has_Incompatible_Version; + public static String MetadataRepositoryIO_Parser_Error_Parsing_Repository; + } diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/MetadataRepositoryIO.java b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/MetadataRepositoryIO.java index b05dec4e1..2e951e17d 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/MetadataRepositoryIO.java +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/MetadataRepositoryIO.java @@ -13,8 +13,22 @@ package org.eclipse.equinox.internal.p2.metadata.repository; import com.thoughtworks.xstream.XStream; import java.io.*; +import java.util.*; +import javax.xml.parsers.ParserConfigurationException; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; +import org.eclipse.equinox.p2.core.helpers.*; import org.eclipse.equinox.p2.core.repository.RepositoryCreationException; +import org.eclipse.equinox.p2.metadata.*; import org.eclipse.equinox.p2.metadata.repository.IMetadataRepository; +import org.eclipse.equinox.spi.p2.metadata.repository.AbstractMetadataRepository; +import org.eclipse.equinox.spi.p2.metadata.repository.AbstractMetadataRepository.RepositoryState; +import org.eclipse.osgi.service.resolver.VersionRange; +import org.eclipse.osgi.util.NLS; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Version; +import org.xml.sax.*; /** * This class reads and writes provisioning metadata. @@ -26,6 +40,8 @@ class MetadataRepositoryIO { * Reads metadata from the given stream, and returns the contained array * of abstract metadata repositories. * This method performs buffering, and closes the stream when finished. + * + * @deprecated */ public static IMetadataRepository read(InputStream input) throws RepositoryCreationException { XStream stream = new XStream(); @@ -43,6 +59,9 @@ class MetadataRepositoryIO { } } + /** + * @deprecated + */ public static void write(IMetadataRepository repository, OutputStream output) { XStream stream = new XStream(); OutputStream bufferedOutput = null; @@ -62,4 +81,880 @@ class MetadataRepositoryIO { e.printStackTrace(); } } + + public void writeNew(IMetadataRepository repository, OutputStream output) { + OutputStream bufferedOutput = null; + try { + try { + bufferedOutput = new BufferedOutputStream(output); + Writer repositoryWriter = new Writer(bufferedOutput, repository.getClass()); + repositoryWriter.write(repository); + } finally { + if (bufferedOutput != null) { + bufferedOutput.close(); + } + } + } catch (IOException ioe) { + // TODO shouldn't this throw a core exception? + ioe.printStackTrace(); + } + } + + /** + * Reads metadata from the given stream, and returns the contained array + * of abstract metadata repositories. + * This method performs buffering, and closes the stream when finished. + */ + public IMetadataRepository readNew(InputStream input) throws RepositoryCreationException { + BufferedInputStream bufferedInput = null; + try { + try { + bufferedInput = new BufferedInputStream(input); + + Parser repositoryParser = new Parser(Activator.getContext(), Activator.ID); + repositoryParser.parse(input); + if (repositoryParser.isValidXML()) { + throw new RepositoryCreationException(new CoreException(repositoryParser.getStatus())); + } + IMetadataRepository theRepository = repositoryParser.getRepository(); + // TODO: temporary - try write after read for comparison: + // see note below about call to writeInstallableUnits(...) + writeNew(theRepository, new FileOutputStream(new File("C:/Ap2/servers2/metadataRepository/writeback.xml"))); //$NON-NLS-1$ + return theRepository; + } finally { + if (bufferedInput != null) + bufferedInput.close(); + } + } catch (IOException ioe) { + throw new RepositoryCreationException(ioe); + } + } + + private interface XMLConstants extends org.eclipse.equinox.p2.core.helpers.XMLConstants { + + // Constants defining the structure of the XML for a MetadataRepository + + // A format version number for metadata repository XML. + public static final String XML_VERSION = "0.0.1"; //$NON-NLS-1$ + public static final Version CURRENT_VERSION = new Version(XML_VERSION); + public static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, CURRENT_VERSION, true); + + // Constants for processing Instructions + public static final String PI_REPOSITORY_TARGET = "metadataRepository"; //$NON-NLS-1$ + //public static XMLWriter.ProcessingInstruction[] PI_DEFAULTS = new XMLWriter.ProcessingInstruction[] {XMLWriter.ProcessingInstruction.makeClassVersionInstruction(PI_REPOSITORY_TARGET, IMetadataRepository.class, CURRENT_VERSION)}; + + // Constants for metadata repository elements + public static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$ + public static final String INSTALLABLE_UNITS_ELEMENT = "units"; //$NON-NLS-1$ + public static final String INSTALLABLE_UNIT_ELEMENT = "unit"; //$NON-NLS-1$ + public static final String PROCESSING_STEPS_ELEMENT = "processing"; //$NON-NLS-1$ + public static final String PROCESSING_STEP_ELEMENT = "step"; //$NON-NLS-1$ + + // Constants for sub-elements of an installable unit element + public static final String ARTIFACT_KEYS_ELEMENT = "artifacts"; //$NON-NLS-1$ + public static final String ARTIFACT_KEY_ELEMENT = "artifact"; //$NON-NLS-1$ + public static final String REQUIRED_CAPABILITIES_ELEMENT = "requires"; //$NON-NLS-1$ + public static final String REQUIRED_CAPABILITY_ELEMENT = "required"; //$NON-NLS-1$ + public static final String PROVIDED_CAPABILITIES_ELEMENT = "provides"; //$NON-NLS-1$ + public static final String PROVIDED_CAPABILITY_ELEMENT = "provided"; //$NON-NLS-1$ + public static final String TOUCHPOINT_TYPE_ELEMENT = "touchpoint"; //$NON-NLS-1$ + public static final String TOUCHPOINT_DATA_ELEMENT = "touchpointData"; //$NON-NLS-1$ + public static final String IU_FILTER_ELEMENT = "filter"; //$NON-NLS-1$ + public static final String APPLICABILITY_FILTER_ELEMENT = "applicability"; //$NON-NLS-1$ + + // Constants for attributes of an installable unit element + public static final String SINGLETON_ATTRIBUTE = "singleton"; //$NON-NLS-1$ + public static final String FRAGMENT_ATTRIBUTE = "fragment"; //$NON-NLS-1$ + + // Constants for attributes of a fragment installable unit element + public static final String FRAGMENT_HOST_ID_ATTRIBUTE = "hostId"; //$NON-NLS-1$ + public static final String FRAGMENT_HOST_RANGE_ATTRIBUTE = "hostRange"; //$NON-NLS-1$ + + // Constants for sub-elements of a required capability element + public static final String CAPABILITY_FILTER_ELEMENT = "filter"; //$NON-NLS-1$ + public static final String CAPABILITY_SELECTORS_ELEMENT = "selectors"; //$NON-NLS-1$ + public static final String CAPABILITY_SELECTOR_ELEMENT = "selector"; //$NON-NLS-1$ + + // Constants for attributes of a required capability element + public static final String CAPABILITY_OPTIONAL_ATTRIBUTE = "optional"; //$NON-NLS-1$ + public static final String CAPABILITY_MULTIPLE_ATTRIBUTE = "multiple"; //$NON-NLS-1$ + + // Constants for attributes of an artifact key element + public static final String ARTIFACT_KEY_NAMESPACE_ATTRIBUTE = NAMESPACE_ATTRIBUTE; + public static final String ARTIFACT_KEY_CLASSIFIER_ATTRIBUTE = "classifier"; //$NON-NLS-1$ + + // Constants for sub-elements of a touchpoint data element + public static final String TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT = "instructions"; //$NON-NLS-1$ + public static final String TOUCHPOINT_DATA_INSTRUCTION_ELEMENT = "instruction"; //$NON-NLS-1$ + + // Constants for attributes of an a touchpoint data instruction element + public static final String TOUCHPOINT_DATA_INSTRUCTION_KEY_ATTRIBUTE = "key"; //$NON-NLS-1$ + + } + + protected XMLWriter.ProcessingInstruction[] createPI(Class repositoryClass) { + return new XMLWriter.ProcessingInstruction[] {XMLWriter.ProcessingInstruction.makeClassVersionInstruction(XMLConstants.PI_REPOSITORY_TARGET, repositoryClass, XMLConstants.CURRENT_VERSION)}; + } + + // XML writer for a IMetadataRepository + protected class Writer extends XMLWriter implements XMLConstants { + + public Writer(OutputStream output, Class repositoryClass) throws IOException { + super(output, createPI(repositoryClass)); + } + + /** + * Write the given metadata repository to the output stream. + */ + public void write(IMetadataRepository repository) { + start(REPOSITORY_ELEMENT); + attribute(NAME_ATTRIBUTE, repository.getName()); + attribute(TYPE_ATTRIBUTE, repository.getType()); + attribute(VERSION_ATTRIBUTE, repository.getVersion()); + attributeOptional(PROVIDER_ATTRIBUTE, repository.getProvider()); + attributeOptional(DESCRIPTION_ATTRIBUTE, repository.getDescription()); // TODO: could be cdata? + + writeProperties(repository.getProperties()); + writeInstallableUnits(getInstallableUnits(repository)); + + end(REPOSITORY_ELEMENT); + flush(); + } + + private IInstallableUnit[] getInstallableUnits(IMetadataRepository repository) { + // TODO: there is probably a better solution to the problem. + // TODO: Because the implementation of IMetadataRepository.getInstallableUnits + // in LocalMetadataRepository uses a query, the order of IUs is not preserved + // write after read. FIX THIS! + Set units = null; + if (repository instanceof LocalMetadataRepository) { + units = ((LocalMetadataRepository) repository).getInstallableUnits(); + } else if (repository instanceof URLMetadataRepository) { + units = ((URLMetadataRepository) repository).getInstallableUnits(); + } else { + return repository.getInstallableUnits(new NullProgressMonitor()); + } + return (units == null ? new IInstallableUnit[0] // + : (IInstallableUnit[]) units.toArray(new IInstallableUnit[units.size()])); + } + + private void writeInstallableUnits(IInstallableUnit[] installableUnits) { + if (installableUnits.length > 0) { + start(INSTALLABLE_UNITS_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, installableUnits.length); + for (int i = 0; i < installableUnits.length; i++) { + writeInstallableUnit(installableUnits[i]); + } + end(INSTALLABLE_UNITS_ELEMENT); + } + } + + private void writeInstallableUnit(IInstallableUnit resolvedIU) { + IInstallableUnit iu = (!(resolvedIU instanceof IResolvedInstallableUnit) ? resolvedIU// + : ((IResolvedInstallableUnit) resolvedIU).getOriginal()); + start(INSTALLABLE_UNIT_ELEMENT); + attribute(ID_ATTRIBUTE, iu.getId()); + attribute(VERSION_ATTRIBUTE, iu.getVersion()); + attribute(SINGLETON_ATTRIBUTE, iu.isSingleton(), true); + attribute(FRAGMENT_ATTRIBUTE, iu.isFragment(), false); + + if (iu.isFragment() && iu instanceof IInstallableUnitFragment) { + IInstallableUnitFragment fragment = (IInstallableUnitFragment) iu; + attribute(FRAGMENT_HOST_ID_ATTRIBUTE, fragment.getHostId()); + attribute(FRAGMENT_HOST_RANGE_ATTRIBUTE, fragment.getHostVersionRange()); + } + + writeProperties(iu.getProperties()); + writeProvidedCapabilities(iu.getProvidedCapabilities()); + writeRequiredCapabilities(iu.getRequiredCapabilities()); + writeTrimmedCdata(IU_FILTER_ELEMENT, iu.getFilter()); + writeTrimmedCdata(APPLICABILITY_FILTER_ELEMENT, iu.getApplicabilityFilter()); + + writeArtifactKeys(iu.getArtifacts()); + writeTouchpointType(iu.getTouchpointType()); + writeTouchpointData(iu.getTouchpointData()); + + end(INSTALLABLE_UNIT_ELEMENT); + } + + private void writeProvidedCapabilities(ProvidedCapability[] capabilities) { + if (capabilities != null && capabilities.length > 0) { + start(PROVIDED_CAPABILITIES_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, capabilities.length); + for (int i = 0; i < capabilities.length; i++) { + start(PROVIDED_CAPABILITY_ELEMENT); + attribute(NAMESPACE_ATTRIBUTE, capabilities[i].getNamespace()); + attribute(NAME_ATTRIBUTE, capabilities[i].getName()); + attribute(VERSION_ATTRIBUTE, capabilities[i].getVersion()); + end(PROVIDED_CAPABILITY_ELEMENT); + } + end(PROVIDED_CAPABILITIES_ELEMENT); + } + } + + private void writeRequiredCapabilities(RequiredCapability[] capabilities) { + if (capabilities != null && capabilities.length > 0) { + start(REQUIRED_CAPABILITIES_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, capabilities.length); + for (int i = 0; i < capabilities.length; i++) { + writeRequiredCapability(capabilities[i]); + } + end(REQUIRED_CAPABILITIES_ELEMENT); + } + } + + private void writeRequiredCapability(RequiredCapability capability) { + start(REQUIRED_CAPABILITY_ELEMENT); + attribute(NAMESPACE_ATTRIBUTE, capability.getNamespace()); + attribute(NAME_ATTRIBUTE, capability.getName()); + attribute(VERSION_RANGE_ATTRIBUTE, capability.getRange()); + attribute(CAPABILITY_OPTIONAL_ATTRIBUTE, capability.isOptional(), false); + attribute(CAPABILITY_MULTIPLE_ATTRIBUTE, capability.isMultiple(), false); + + writeTrimmedCdata(CAPABILITY_FILTER_ELEMENT, capability.getFilter()); + + String[] selectors = capability.getSelectors(); + if (selectors.length > 0) { + start(CAPABILITY_SELECTORS_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, selectors.length); + for (int j = 0; j < selectors.length; j++) { + writeTrimmedCdata(CAPABILITY_SELECTOR_ELEMENT, selectors[j]); + } + end(CAPABILITY_SELECTORS_ELEMENT); + } + + end(REQUIRED_CAPABILITY_ELEMENT); + } + + private void writeArtifactKeys(IArtifactKey[] artifactKeys) { + if (artifactKeys != null && artifactKeys.length > 0) { + start(ARTIFACT_KEYS_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, artifactKeys.length); + for (int i = 0; i < artifactKeys.length; i++) { + start(ARTIFACT_KEY_ELEMENT); + attribute(ARTIFACT_KEY_NAMESPACE_ATTRIBUTE, artifactKeys[i].getNamespace()); + attribute(ARTIFACT_KEY_CLASSIFIER_ATTRIBUTE, artifactKeys[i].getClassifier()); + attribute(ID_ATTRIBUTE, artifactKeys[i].getId()); + attribute(VERSION_ATTRIBUTE, artifactKeys[i].getVersion()); + end(ARTIFACT_KEY_ELEMENT); + } + end(ARTIFACT_KEYS_ELEMENT); + } + } + + private void writeTouchpointType(TouchpointType touchpointType) { + start(TOUCHPOINT_TYPE_ELEMENT); + attribute(ID_ATTRIBUTE, touchpointType.getId()); + attribute(VERSION_ATTRIBUTE, touchpointType.getVersion()); + end(TOUCHPOINT_TYPE_ELEMENT); + } + + private void writeTouchpointData(TouchpointData[] touchpointData) { + if (touchpointData != null && touchpointData.length > 0) { + start(TOUCHPOINT_DATA_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, touchpointData.length); + for (int i = 0; i < touchpointData.length; i++) { + TouchpointData nextData = touchpointData[i]; + Map instructions = nextData.getInstructions(); + if (instructions.size() > 0) { + start(TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT); + attribute(COLLECTION_SIZE_ATTRIBUTE, instructions.size()); + for (Iterator iter = instructions.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + start(TOUCHPOINT_DATA_INSTRUCTION_ELEMENT); + attribute(TOUCHPOINT_DATA_INSTRUCTION_KEY_ATTRIBUTE, entry.getKey()); + cdataLines((String) entry.getValue(), true); + end(TOUCHPOINT_DATA_INSTRUCTION_ELEMENT); + } + } + } + end(TOUCHPOINT_DATA_ELEMENT); + } + } + + private void writeTrimmedCdata(String element, String filter) { + String trimmed; + if (filter != null && (trimmed = filter.trim()).length() > 0) { + start(element); + cdata(trimmed); + end(element); + } + } + } + + /* + * Parser for the contents of a LocalMetadata, + * as written by the Writer class. + */ + private class Parser extends XMLParser implements XMLConstants { + + private IMetadataRepository theRepository = null; + protected Class theRepositoryClass = LocalMetadataRepository.class; + + public Parser(BundleContext context, String bundleId) { + super(context, bundleId); + } + + public void parse(File file) throws IOException { + parse(new FileInputStream(file)); + } + + public synchronized void parse(InputStream stream) throws IOException { + this.status = null; + try { + // TODO: currently not caching the parser since we make no assumptions + // or restrictions on concurrent parsing + getParser(); + RepositoryHandler repositoryHandler = new RepositoryHandler(); + xmlReader.setContentHandler(new RepositoryDocHandler(REPOSITORY_ELEMENT, repositoryHandler)); + xmlReader.parse(new InputSource(stream)); + if (isValidXML()) { + theRepository = repositoryHandler.getRepository(); + } + } catch (SAXException e) { + throw new IOException(e.getMessage()); + } catch (ParserConfigurationException e) { + throw new IOException(e.getMessage()); + } finally { + stream.close(); + } + } + + public IMetadataRepository getRepository() { + return theRepository; + } + + public Class getRepositoryClass() { + return theRepositoryClass; + } + + protected Object getRootObject() { + return theRepository; + } + + public void ProcessingInstruction(String target, String data) throws SAXException { + if (PI_REPOSITORY_TARGET.equalsIgnoreCase(target)) { + // TODO: should the root handler be constructed based on class + // via an extension registry mechanism? + String clazz = extractPIClass(data); + try { + theRepositoryClass = Class.forName(clazz); + } catch (ClassNotFoundException e) { + // throw new SAXException(NLS.bind(Messages.MetadataRepositoryIO_Repository_Class_Not_Found, clazz)); + throw new SAXException("Metadata repository class " + clazz + "not found"); //$NON-NLS-1$//$NON-NLS-2$ + } + + // TODO: version tolerance by extension + Version repositoryVersion = extractPIVersion(target, data); + if (!XML_TOLERANCE.isIncluded(repositoryVersion)) { + throw new SAXException(NLS.bind(Messages.MetadataRepositoryIO_Parser_Has_Incompatible_Version, repositoryVersion, XML_TOLERANCE)); + } + } + } + + private final class RepositoryDocHandler extends DocHandler { + + public RepositoryDocHandler(String rootName, RootHandler rootHandler) { + super(rootName, rootHandler); + } + } + + private final class RepositoryHandler extends RootHandler { + + private final String[] required = new String[] {NAME_ATTRIBUTE, TYPE_ATTRIBUTE, VERSION_ATTRIBUTE}; + private final String[] optional = new String[] {DESCRIPTION_ATTRIBUTE, PROVIDER_ATTRIBUTE}; + + private InstallableUnitsHandler unitsHandler = null; + private PropertiesHandler propertiesHandler = null; + + private AbstractMetadataRepository repository = null; + + private RepositoryState state = new RepositoryState(); + + public RepositoryHandler() { + super(); + } + + public IMetadataRepository getRepository() { + return repository; + } + + protected void handleRootAttributes(Attributes attributes) { + String[] values = parseAttributes(attributes, required, optional); + Version version = checkVersion(this.elementHandled, VERSION_ATTRIBUTE, values[2]); + state.Name = values[0]; + state.Type = values[1]; + state.Version = version; + state.Description = values[3]; + state.Provider = values[4]; + state.Location = null; // new URL("file://C:/bogus"); + } + + public void startElement(String name, Attributes attributes) { + if (PROPERTIES_ELEMENT.equalsIgnoreCase(name)) { + if (propertiesHandler == null) { + propertiesHandler = new PropertiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (INSTALLABLE_UNITS_ELEMENT.equalsIgnoreCase(name)) { + if (unitsHandler == null) { + unitsHandler = new InstallableUnitsHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else { + invalidElement(name, attributes); + } + } + + protected void finished() { + if (isValidXML()) { + // // TODO: need to support URLMetadataRepository as well. + // try { + // repository = new LocalMetadataRepository(attrValues[0], attrValues[1], attrValues[2], new URL("file://C:/bogus"), attrValues[3], attrValues[4]); + // } catch (MalformedURLException e) { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + state.Properties = (propertiesHandler == null ? new OrderedProperties(0) // + : propertiesHandler.getProperties()); + state.Units = (unitsHandler == null ? new IInstallableUnit[0] // + : unitsHandler.getUnits()); + try { + Object repositoryObject = theRepositoryClass.newInstance(); + if (repositoryObject instanceof AbstractMetadataRepository) { + repository = (AbstractMetadataRepository) repositoryObject; + repository.initialize(state); + } + } catch (InstantiationException e) { + // TODO: Throw a SAXException + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO: Throw a SAXException + e.printStackTrace(); + } + } + } + } + + protected class InstallableUnitsHandler extends AbstractHandler { + + private ArrayList units; + + public InstallableUnitsHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, INSTALLABLE_UNITS_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + units = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public IInstallableUnit[] getUnits() { + return (IInstallableUnit[]) units.toArray(new IInstallableUnit[units.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(INSTALLABLE_UNIT_ELEMENT)) { + new InstallableUnitHandler(this, attributes, units); + } else { + invalidElement(name, attributes); + } + } + } + + protected class InstallableUnitHandler extends AbstractHandler { + + private final String[] required = new String[] {ID_ATTRIBUTE, VERSION_ATTRIBUTE}; + private final String[] optional = new String[] {SINGLETON_ATTRIBUTE, FRAGMENT_ATTRIBUTE, FRAGMENT_HOST_ID_ATTRIBUTE, FRAGMENT_HOST_RANGE_ATTRIBUTE}; + + InstallableUnit currentUnit = null; + + private PropertiesHandler propertiesHandler = null; + private ProvidedCapabilitiesHandler providedCapabilitiesHandler = null; + private RequiredCapabilitiesHandler requiredCapabilitiesHandler = null; + private TextHandler filterHandler = null; + private TextHandler applicabilityHandler = null; + private ArtifactsHandler artifactsHandler = null; + private TouchpointTypeHandler touchpointTypeHandler = null; + private TouchpointDataHandler touchpointDataHandler = null; + + public InstallableUnitHandler(AbstractHandler parentHandler, Attributes attributes, List units) { + super(parentHandler, INSTALLABLE_UNIT_ELEMENT); + String[] values = parseAttributes(attributes, required, optional); + + Version version = checkVersion(INSTALLABLE_UNIT_ELEMENT, VERSION_ATTRIBUTE, values[1]); + boolean singleton = checkBoolean(INSTALLABLE_UNIT_ELEMENT, SINGLETON_ATTRIBUTE, values[2], true).booleanValue(); + boolean isFragment = checkBoolean(INSTALLABLE_UNIT_ELEMENT, FRAGMENT_ATTRIBUTE, values[3], false).booleanValue(); + if (isFragment) { + // TODO: tooling default fragment does not have a host id + // checkRequiredAttribute(INSTALLABLE_UNIT_ELEMENT, FRAGMENT_HOST_ID_ATTRIBUTE, values[4]); + checkRequiredAttribute(INSTALLABLE_UNIT_ELEMENT, FRAGMENT_HOST_RANGE_ATTRIBUTE, values[5]); + VersionRange hostRange = checkVersionRange(INSTALLABLE_UNIT_ELEMENT, FRAGMENT_HOST_RANGE_ATTRIBUTE, values[5]); + currentUnit = new InstallableUnitFragment(values[0], version, singleton, values[4], hostRange); + } else { + if (values[4] != null) { + unexpectedAttribute(INSTALLABLE_UNIT_ELEMENT, FRAGMENT_HOST_ID_ATTRIBUTE, values[4]); + } else if (values[5] != null) { + unexpectedAttribute(INSTALLABLE_UNIT_ELEMENT, FRAGMENT_HOST_RANGE_ATTRIBUTE, values[4]); + } + currentUnit = new InstallableUnit(values[0], version, singleton); + } + units.add(currentUnit); + } + + public IInstallableUnit getInstallableUnit() { + return currentUnit; + } + + public void startElement(String name, Attributes attributes) { + if (PROPERTIES_ELEMENT.equalsIgnoreCase(name)) { + if (propertiesHandler == null) { + propertiesHandler = new PropertiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (PROVIDED_CAPABILITIES_ELEMENT.equalsIgnoreCase(name)) { + if (providedCapabilitiesHandler == null) { + providedCapabilitiesHandler = new ProvidedCapabilitiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (REQUIRED_CAPABILITIES_ELEMENT.equalsIgnoreCase(name)) { + if (requiredCapabilitiesHandler == null) { + requiredCapabilitiesHandler = new RequiredCapabilitiesHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (IU_FILTER_ELEMENT.equalsIgnoreCase(name)) { + if (filterHandler == null) { + filterHandler = new TextHandler(this, IU_FILTER_ELEMENT, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (APPLICABILITY_FILTER_ELEMENT.equalsIgnoreCase(name)) { + if (applicabilityHandler == null) { + applicabilityHandler = new TextHandler(this, APPLICABILITY_FILTER_ELEMENT, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (ARTIFACT_KEYS_ELEMENT.equalsIgnoreCase(name)) { + if (artifactsHandler == null) { + artifactsHandler = new ArtifactsHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (TOUCHPOINT_TYPE_ELEMENT.equalsIgnoreCase(name)) { + if (touchpointTypeHandler == null) { + touchpointTypeHandler = new TouchpointTypeHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else if (TOUCHPOINT_DATA_ELEMENT.equalsIgnoreCase(name)) { + if (touchpointDataHandler == null) { + touchpointDataHandler = new TouchpointDataHandler(this, attributes); + } else { + duplicateElement(this, name, attributes); + } + } else { + invalidElement(name, attributes); + } + } + + protected void finished() { + if (isValidXML() && currentUnit != null) { + OrderedProperties properties = (propertiesHandler == null ? new OrderedProperties(0) // + : propertiesHandler.getProperties()); + currentUnit.addProperties(properties); + ProvidedCapability[] providedCapabilities = (providedCapabilitiesHandler == null ? new ProvidedCapability[0] // + : providedCapabilitiesHandler.getProvidedCapabilities()); + currentUnit.setCapabilities(providedCapabilities); + RequiredCapability[] requiredCapabilities = (requiredCapabilitiesHandler == null ? new RequiredCapability[0] // + : requiredCapabilitiesHandler.getRequiredCapabilities()); + currentUnit.setRequiredCapabilities(requiredCapabilities); + if (filterHandler != null) { + currentUnit.setFilter(filterHandler.getText()); + } + if (applicabilityHandler != null) { + currentUnit.setApplicabilityFilter(applicabilityHandler.getText()); + } + IArtifactKey[] artifacts = (artifactsHandler == null ? new IArtifactKey[0] // + : artifactsHandler.getArtifactKeys()); + currentUnit.setArtifacts(artifacts); + if (touchpointTypeHandler != null) { + currentUnit.setTouchpointType(touchpointTypeHandler.getTouchpointType()); + } else { + // TODO: create an error + } + TouchpointData[] touchpointData = (touchpointDataHandler == null ? new TouchpointData[0] // + : touchpointDataHandler.getTouchpointData()); + currentUnit.addTouchpointData(touchpointData); + } + } + } + + protected class ProvidedCapabilitiesHandler extends AbstractHandler { + + private List providedCapabilities; + + public ProvidedCapabilitiesHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, PROVIDED_CAPABILITIES_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + providedCapabilities = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public ProvidedCapability[] getProvidedCapabilities() { + return (ProvidedCapability[]) providedCapabilities.toArray(new ProvidedCapability[providedCapabilities.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(PROVIDED_CAPABILITY_ELEMENT)) { + new ProvidedCapabilityHandler(this, attributes, providedCapabilities); + } else { + invalidElement(name, attributes); + } + } + } + + protected class ProvidedCapabilityHandler extends AbstractHandler { + + private final String[] required = new String[] {NAMESPACE_ATTRIBUTE, NAME_ATTRIBUTE, VERSION_ATTRIBUTE}; + + public ProvidedCapabilityHandler(AbstractHandler parentHandler, Attributes attributes, List capabilities) { + super(parentHandler, PROVIDED_CAPABILITY_ELEMENT); + String[] values = parseRequiredAttributes(attributes, required); + Version version = checkVersion(PROVIDED_CAPABILITY_ELEMENT, VERSION_ATTRIBUTE, values[2]); + capabilities.add(new ProvidedCapability(values[0], values[1], version)); + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + } + + protected class RequiredCapabilitiesHandler extends AbstractHandler { + + private List requiredCapabilities; + + public RequiredCapabilitiesHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, REQUIRED_CAPABILITIES_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + requiredCapabilities = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public RequiredCapability[] getRequiredCapabilities() { + return (RequiredCapability[]) requiredCapabilities.toArray(new RequiredCapability[requiredCapabilities.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(REQUIRED_CAPABILITY_ELEMENT)) { + new RequiredCapabilityHandler(this, attributes, requiredCapabilities); + } else { + invalidElement(name, attributes); + } + } + } + + protected class RequiredCapabilityHandler extends AbstractHandler { + + private final String[] required = new String[] {NAMESPACE_ATTRIBUTE, NAME_ATTRIBUTE, VERSION_RANGE_ATTRIBUTE}; + private final String[] optional = new String[] {CAPABILITY_OPTIONAL_ATTRIBUTE, CAPABILITY_MULTIPLE_ATTRIBUTE}; + + private RequiredCapability currentCapability = null; + + private TextHandler filterHandler = null; + private CapabilitySelectorsHandler selectorsHandler = null; + + public RequiredCapabilityHandler(AbstractHandler parentHandler, Attributes attributes, List capabilities) { + super(parentHandler, REQUIRED_CAPABILITY_ELEMENT); + String[] values = parseAttributes(attributes, required, optional); + VersionRange range = checkVersionRange(REQUIRED_CAPABILITY_ELEMENT, VERSION_RANGE_ATTRIBUTE, values[2]); + boolean isOptional = checkBoolean(REQUIRED_CAPABILITY_ELEMENT, CAPABILITY_OPTIONAL_ATTRIBUTE, values[3], false).booleanValue(); + boolean isMultiple = checkBoolean(REQUIRED_CAPABILITY_ELEMENT, CAPABILITY_MULTIPLE_ATTRIBUTE, values[4], false).booleanValue(); + currentCapability = new RequiredCapability(values[0], values[1], range, null, isOptional, isMultiple); + capabilities.add(currentCapability); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(CAPABILITY_FILTER_ELEMENT)) { + filterHandler = new TextHandler(this, CAPABILITY_FILTER_ELEMENT, attributes); + } else if (name.equalsIgnoreCase(CAPABILITY_SELECTORS_ELEMENT)) { + selectorsHandler = new CapabilitySelectorsHandler(this, attributes); + } else { + invalidElement(name, attributes); + } + } + + protected void finished() { + if (isValidXML()) { + if (currentCapability != null) { + if (filterHandler != null) { + currentCapability.setFilter(filterHandler.getText()); + } + if (selectorsHandler != null) { + currentCapability.setSelectors(selectorsHandler.getSelectors()); + } + } + } + } + } + + protected class ArtifactsHandler extends AbstractHandler { + + private List artifacts; + + public ArtifactsHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, ARTIFACT_KEYS_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + artifacts = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public IArtifactKey[] getArtifactKeys() { + return (IArtifactKey[]) artifacts.toArray(new IArtifactKey[artifacts.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(ARTIFACT_KEY_ELEMENT)) { + new ArtifactHandler(this, attributes, artifacts); + } else { + invalidElement(name, attributes); + } + } + } + + protected class ArtifactHandler extends AbstractHandler { + + private final String[] required = new String[] {NAMESPACE_ATTRIBUTE, CLASSIFIER_ATTRIBUTE, ID_ATTRIBUTE, VERSION_ATTRIBUTE}; + + public ArtifactHandler(AbstractHandler parentHandler, Attributes attributes, List artifacts) { + super(parentHandler, ARTIFACT_KEY_ELEMENT); + String[] values = parseRequiredAttributes(attributes, required); + Version version = checkVersion(ARTIFACT_KEY_ELEMENT, VERSION_ATTRIBUTE, values[3]); + artifacts.add(new ArtifactKey(values[0], values[1], values[2], version)); + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + } + + protected class CapabilitySelectorsHandler extends AbstractHandler { + + private List selectors; + + public CapabilitySelectorsHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, CAPABILITY_SELECTORS_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + selectors = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public String[] getSelectors() { + return (String[]) selectors.toArray(new String[selectors.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(CAPABILITY_SELECTOR_ELEMENT)) { + new TextHandler(this, CAPABILITY_SELECTOR_ELEMENT, attributes, selectors); + } else { + invalidElement(name, attributes); + } + } + } + + protected class TouchpointTypeHandler extends AbstractHandler { + + private final String[] required = new String[] {ID_ATTRIBUTE, VERSION_ATTRIBUTE}; + + TouchpointType touchpointType = null; + + public TouchpointTypeHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, TOUCHPOINT_TYPE_ELEMENT); + String[] values = parseRequiredAttributes(attributes, required); + Version version = checkVersion(TOUCHPOINT_TYPE_ELEMENT, VERSION_ATTRIBUTE, values[1]); + touchpointType = new TouchpointType(values[0], version); + } + + public TouchpointType getTouchpointType() { + return touchpointType; + } + + public void startElement(String name, Attributes attributes) { + invalidElement(name, attributes); + } + } + + protected class TouchpointDataHandler extends AbstractHandler { + + TouchpointData touchpointData = null; + + List data = null; + + public TouchpointDataHandler(AbstractHandler parentHandler, Attributes attributes) { + super(parentHandler, TOUCHPOINT_DATA_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + data = (size != null ? new ArrayList(new Integer(size).intValue()) : new ArrayList(4)); + } + + public TouchpointData[] getTouchpointData() { + return (TouchpointData[]) data.toArray(new TouchpointData[data.size()]); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT)) { + new TouchpointInstructionsHandler(this, attributes, data); + } else { + invalidElement(name, attributes); + } + } + } + + protected class TouchpointInstructionsHandler extends AbstractHandler { + + Map instructions = null; + + public TouchpointInstructionsHandler(AbstractHandler parentHandler, Attributes attributes, List data) { + super(parentHandler, TOUCHPOINT_DATA_INSTRUCTIONS_ELEMENT); + String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); + instructions = (size != null ? new LinkedHashMap(new Integer(size).intValue()) : new LinkedHashMap(4)); + data.add(new TouchpointData(instructions)); + } + + public void startElement(String name, Attributes attributes) { + if (name.equalsIgnoreCase(TOUCHPOINT_DATA_INSTRUCTION_ELEMENT)) { + new TouchpointInstructionHandler(this, attributes, instructions); + } else { + invalidElement(name, attributes); + } + } + } + + protected class TouchpointInstructionHandler extends TextHandler { + + private final String[] required = new String[] {TOUCHPOINT_DATA_INSTRUCTION_KEY_ATTRIBUTE}; + + Map instructions = null; + String key = null; + + public TouchpointInstructionHandler(AbstractHandler parentHandler, Attributes attributes, Map instructions) { + super(parentHandler, TOUCHPOINT_DATA_INSTRUCTION_ELEMENT); + key = parseRequiredAttributes(attributes, required)[0]; + this.instructions = instructions; + } + + protected void finished() { + if (isValidXML()) { + if (key != null) { + instructions.put(key, getText()); + } + } + } + } + + protected String getErrorMessage() { + return Messages.MetadataRepositoryIO_Parser_Error_Parsing_Repository; + } + + public String toString() { + // TODO: + return null; + } + } } diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/URLMetadataRepository.java b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/URLMetadataRepository.java index 9fee1d394..06129b76a 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/URLMetadataRepository.java +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/URLMetadataRepository.java @@ -13,14 +13,12 @@ package org.eclipse.equinox.internal.p2.metadata.repository; import java.net.MalformedURLException; import java.net.URL; -import java.util.HashSet; -import java.util.Iterator; +import java.util.*; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.p2.core.repository.RepositoryCreationException; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.RequiredCapability; -import org.eclipse.equinox.p2.metadata.repository.IMetadataRepository; import org.eclipse.equinox.p2.query.CompoundIterator; import org.eclipse.equinox.spi.p2.metadata.repository.AbstractMetadataRepository; import org.eclipse.osgi.service.resolver.VersionRange; @@ -29,14 +27,14 @@ import org.eclipse.osgi.util.NLS; /** * A metadata repository backed by an arbitrary URL. */ -public class URLMetadataRepository extends AbstractMetadataRepository implements IMetadataRepository { +public class URLMetadataRepository extends AbstractMetadataRepository { static final private String REPOSITORY_TYPE = URLMetadataRepository.class.getName(); static final private Integer REPOSITORY_VERSION = new Integer(1); static final protected String CONTENT_FILENAME = "content.xml"; //$NON-NLS-1$ transient protected URL content; - protected HashSet units = new HashSet(); + protected HashSet units = new LinkedHashSet(); public static URL getActualLocation(URL base) { String spec = base.toExternalForm(); @@ -58,10 +56,14 @@ public class URLMetadataRepository extends AbstractMetadataRepository implements } public URLMetadataRepository(URL location, String name) { - super(name == null ? (location != null ? location.toExternalForm() : "") : name, REPOSITORY_TYPE, REPOSITORY_VERSION.toString(), location, null, null); + super(name == null ? (location != null ? location.toExternalForm() : "") : name, REPOSITORY_TYPE, REPOSITORY_VERSION.toString(), location, null, null); //$NON-NLS-1$ content = getActualLocation(location); } + public URLMetadataRepository() { + super(); + } + protected boolean load() throws RepositoryCreationException { return new SimpleMetadataRepositoryFactory().load(location) != null; } @@ -106,4 +108,21 @@ public class URLMetadataRepository extends AbstractMetadataRepository implements public boolean isModifiable() { return false; } + + // Get a non-modifiable collection of the installable units + // from the repository. + public Set getInstallableUnits() { + return Collections.unmodifiableSet(units); + } + + public void initialize(RepositoryState state) { + this.name = state.Name; + this.type = state.Type; + this.version = state.Version.toString(); + this.provider = state.Provider; + this.description = state.Description; + this.location = state.Location; + this.properties = state.Properties; + this.units.addAll(Arrays.asList(state.Units)); + } } diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/messages.properties b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/messages.properties index fc369d7ab..cf9c00596 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/messages.properties +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/internal/p2/metadata/repository/messages.properties @@ -10,4 +10,9 @@ ############################################################################### REPO_LOADING = Loading the repository {0} -REPOMGR_ADDING_REPO = Adding repository {0}
\ No newline at end of file +REPOMGR_ADDING_REPO = Adding repository {0} + +MetadataRepositoryIO_Parser_Has_Incompatible_Version=\ + Metadata repository has incompatible version {0}; expected {1} +MetadataRepositoryIO_Parser_Error_Parsing_Repository=\ + Error parsing metadata repository diff --git a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/spi/p2/metadata/repository/AbstractMetadataRepository.java b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/spi/p2/metadata/repository/AbstractMetadataRepository.java index 99fc32ea5..046dedb2d 100644 --- a/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/spi/p2/metadata/repository/AbstractMetadataRepository.java +++ b/bundles/org.eclipse.equinox.p2.metadata.repository/src/org/eclipse/equinox/spi/p2/metadata/repository/AbstractMetadataRepository.java @@ -11,12 +11,31 @@ package org.eclipse.equinox.spi.p2.metadata.repository; import java.net.URL; +import org.eclipse.equinox.p2.core.helpers.OrderedProperties; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.repository.IMetadataRepository; import org.eclipse.equinox.spi.p2.core.repository.AbstractRepository; +import org.osgi.framework.Version; public abstract class AbstractMetadataRepository extends AbstractRepository implements IMetadataRepository { + public static class RepositoryState { + public String Name; + public String Type; + public Version Version; + public String Provider; + public String Description; + public URL Location; + public OrderedProperties Properties; + public IInstallableUnit[] Units; + } + + public AbstractMetadataRepository() { + super("noName", "noType", "noVersion", null, null, null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + public abstract void initialize(RepositoryState state); + protected AbstractMetadataRepository(String name, String type, String version, URL location, String description, String provider) { super(name, type, version, location, description, provider); } diff --git a/bundles/org.eclipse.equinox.p2.metadata/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.metadata/META-INF/MANIFEST.MF index 1eb5ca45b..128f69c30 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.metadata/META-INF/MANIFEST.MF @@ -5,7 +5,7 @@ Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-Version: 0.1.0.qualifier -Export-Package: org.eclipse.equinox.internal.p2.metadata;x-friends:="org.eclipse.equinox.p2.metadata.generator", +Export-Package: org.eclipse.equinox.internal.p2.metadata;x-friends:="org.eclipse.equinox.p2.metadata.generator, org.eclipse.equinox.p2.metadata.repository", org.eclipse.equinox.p2.metadata, org.eclipse.equinox.p2.query, org.eclipse.equinox.p2.resolution 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 index eeb5e5f2f..0849155a3 100644 --- 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 @@ -8,6 +8,7 @@ ******************************************************************************/ package org.eclipse.equinox.p2.metadata; +import org.eclipse.equinox.p2.core.helpers.OrderedProperties; import org.osgi.framework.Version; public interface IInstallableUnit extends Comparable { @@ -62,6 +63,8 @@ public interface IInstallableUnit extends Comparable { public abstract String getProperty(String key); + public abstract OrderedProperties getProperties(); + public abstract TouchpointData[] getTouchpointData(); public abstract boolean isFragment(); diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnit.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnit.java index f0873c0c0..c2b49c6fe 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnit.java +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnit.java @@ -8,6 +8,7 @@ ******************************************************************************/ package org.eclipse.equinox.p2.metadata; +import java.util.ArrayList; import org.eclipse.equinox.internal.p2.metadata.InternalInstallableUnit; import org.eclipse.equinox.p2.core.helpers.OrderedProperties; import org.eclipse.equinox.p2.core.helpers.UnmodifiableProperties; @@ -26,7 +27,7 @@ public class InstallableUnit implements IInstallableUnitConstants, IInstallableU private IArtifactKey[] artifacts; private TouchpointType touchpointType; - private TouchpointData immutableTouchpointData; + private ArrayList touchpointData = null; private RequiredCapability[] requires; @@ -40,6 +41,14 @@ public class InstallableUnit implements IInstallableUnitConstants, IInstallableU super(); } + public InstallableUnit(String id, Version version, boolean singleton) { + super(); + this.id = id; + this.version = version.toString(); + this.versionObject = version; + this.singleton = singleton; + } + public TouchpointType getTouchpointType() { return touchpointType == null ? TouchpointType.NONE : touchpointType; } @@ -122,10 +131,7 @@ public class InstallableUnit implements IInstallableUnitConstants, IInstallableU public String setProperty(String key, String value) { if (value == null) - if (properties == null) - return null; - else - return (String) properties.remove(key); + return (properties != null ? (String) properties.remove(key) : null); if (properties == null) properties = new OrderedProperties(); return (String) properties.setProperty(key, value); @@ -169,15 +175,34 @@ public class InstallableUnit implements IInstallableUnitConstants, IInstallableU } public TouchpointData[] getTouchpointData() { - return immutableTouchpointData == null ? TouchpointData.NO_TOUCHPOINT_DATA : new TouchpointData[] {immutableTouchpointData}; + return (touchpointData == null ? TouchpointData.NO_TOUCHPOINT_DATA // + : (TouchpointData[]) touchpointData.toArray(new TouchpointData[touchpointData.size()])); } + // TODO: resolve the schizophrenia between the singleton immutable data + // and the public returned touchpoint data array. public void setImmutableTouchpointData(TouchpointData immutableData) { - this.immutableTouchpointData = immutableData; + ensureTouchpointDataCapacity(4); + touchpointData.add(immutableData); } - OrderedProperties getProperties() { - return properties == null ? NO_PROPERTIES : properties; + public void addTouchpointData(TouchpointData[] newData) { + ensureTouchpointDataCapacity(newData.length); + for (int i = 0; i < newData.length; i++) { + touchpointData.add(newData[i]); + } + } + + private void ensureTouchpointDataCapacity(int size) { + if (touchpointData != null) { + touchpointData.ensureCapacity(size); + } else { + touchpointData = new ArrayList(size); + } + } + + private OrderedProperties properties() { + return (properties != null ? properties : NO_PROPERTIES); } /** @@ -186,8 +211,14 @@ public class InstallableUnit implements IInstallableUnitConstants, IInstallableU * * @return an <i>unmodifiable copy</i> of the IU properties. */ - public OrderedProperties copyProperties() { - return (properties != null ? new UnmodifiableProperties(getProperties()) : new UnmodifiableProperties(new OrderedProperties())); + public OrderedProperties getProperties() { + return new UnmodifiableProperties(properties()); + } + + public void addProperties(OrderedProperties newProperties) { + if (properties == null) + properties = new OrderedProperties(newProperties.size()); + properties.putAll(newProperties); } public boolean isFragment() { diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnitFragment.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnitFragment.java index 49383ab34..e60f6abed 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnitFragment.java +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/InstallableUnitFragment.java @@ -13,13 +13,25 @@ package org.eclipse.equinox.p2.metadata; import org.eclipse.osgi.service.resolver.VersionRange; import org.osgi.framework.Version; -public class InstallableUnitFragment extends InstallableUnit implements IInstallableUnitFragment, IInstallableUnit { - public static ProvidedCapability FRAGMENT_CAPABILITY = new ProvidedCapability(IU_KIND_NAMESPACE, "iu.fragment", new Version(1, 0, 0)); +public class InstallableUnitFragment extends InstallableUnit implements IInstallableUnitFragment { + + public static ProvidedCapability FRAGMENT_CAPABILITY = new ProvidedCapability(IU_KIND_NAMESPACE, "iu.fragment", new Version(1, 0, 0)); //$NON-NLS-1$ private String hostId; private transient VersionRange hostRangeObject; private String hostRange; + public InstallableUnitFragment() { + super(); + } + + public InstallableUnitFragment(String id, Version version, boolean singleton, String hostId, VersionRange hostRange) { + super(id, version, singleton); + this.hostId = hostId; + this.hostRange = hostRange.toString(); + hostRangeObject = hostRange; + } + public ProvidedCapability[] getProvidedCapabilities() { ProvidedCapability[] otherCapabilities = super.getProvidedCapabilities(); if (otherCapabilities.length == 0) diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/RequiredCapability.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/RequiredCapability.java index 7eae09c6f..48f8ac00c 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/RequiredCapability.java +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/RequiredCapability.java @@ -26,13 +26,13 @@ import org.eclipse.osgi.service.resolver.VersionRange; * @see InstallableUnit#IU_NAMESPACE */ public class RequiredCapability { - private String filter; - private boolean multiple; private String name; private String namespace; - boolean optional; private String range; + private boolean optional; + private boolean multiple; + private String filter; private String[] selectors; private transient VersionRange rangeObject; @@ -73,6 +73,10 @@ public class RequiredCapability { return filter; } + public void setFilter(String filter) { + this.filter = filter; + } + public String getName() { return name; } @@ -96,7 +100,7 @@ public class RequiredCapability { } public String toString() { - return "requiredCapability: " + namespace + '/' + name + '/' + range; + return "requiredCapability: " + namespace + '/' + name + '/' + range; //$NON-NLS-1$ } public int hashCode() { @@ -155,4 +159,8 @@ public class RequiredCapability { public String[] getSelectors() { return selectors; } + + public void setSelectors(String[] selectors) { + this.selectors = selectors; + } } diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ResolvedInstallableUnit.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ResolvedInstallableUnit.java index 6205b90af..88b6d96d8 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ResolvedInstallableUnit.java +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/ResolvedInstallableUnit.java @@ -11,6 +11,7 @@ package org.eclipse.equinox.p2.metadata; import java.util.ArrayList; import java.util.Arrays; import org.eclipse.equinox.internal.p2.metadata.InternalInstallableUnit; +import org.eclipse.equinox.p2.core.helpers.OrderedProperties; import org.osgi.framework.Version; public class ResolvedInstallableUnit implements IResolvedInstallableUnit, InternalInstallableUnit { @@ -57,6 +58,10 @@ public class ResolvedInstallableUnit implements IResolvedInstallableUnit, Intern return resolved.getProperty(key); } + public OrderedProperties getProperties() { + return resolved.getProperties(); + } + public ProvidedCapability[] getProvidedCapabilities() { ArrayList result = new ArrayList(); result.addAll(Arrays.asList(resolved.getProvidedCapabilities())); @@ -124,7 +129,7 @@ public class ResolvedInstallableUnit implements IResolvedInstallableUnit, Intern } public String toString() { - return "[R]" + resolved.toString(); + return "[R]" + resolved.toString(); //$NON-NLS-1$ } public IInstallableUnit getOriginal() { diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/TouchpointData.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/TouchpointData.java index 7bfcae9e3..58c0f8590 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/TouchpointData.java +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/metadata/TouchpointData.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.equinox.p2.metadata; +import java.util.Collections; import java.util.Map; public class TouchpointData { @@ -52,4 +53,10 @@ public class TouchpointData { public String getInstructions(String instructionKey) { return (String) instructions.get(instructionKey); } + + // Return an unmodifiable collection of the instructions + // in the touchpoint data. + public Map getInstructions() { + return Collections.unmodifiableMap(instructions); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestMetadataRepository.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestMetadataRepository.java index db639f22f..ce1ca7384 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestMetadataRepository.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/TestMetadataRepository.java @@ -93,4 +93,15 @@ public class TestMetadataRepository extends AbstractMetadataRepository { units.remove(toRemove[i]); } } + + public void initialize(RepositoryState state) { + this.name = state.Name; + this.type = state.Type; + this.version = state.Version.toString(); + this.provider = state.Provider; + this.description = state.Description; + this.location = state.Location; + this.properties = state.Properties; + this.units.addAll(Arrays.asList(state.Units)); + } } |