| /******************************************************************************* |
| * Copyright (c) 2007, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.p2.artifact.repository.simple; |
| |
| import java.io.*; |
| import java.net.URL; |
| import java.util.*; |
| import javax.xml.parsers.ParserConfigurationException; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.artifact.repository.Activator; |
| import org.eclipse.equinox.internal.p2.artifact.repository.Messages; |
| import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; |
| import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties; |
| import org.eclipse.equinox.internal.p2.metadata.ArtifactKey; |
| import org.eclipse.equinox.internal.p2.persistence.XMLParser; |
| import org.eclipse.equinox.internal.p2.persistence.XMLWriter; |
| import org.eclipse.equinox.internal.provisional.p2.artifact.repository.ArtifactDescriptor; |
| import org.eclipse.equinox.internal.provisional.p2.artifact.repository.IArtifactRepository; |
| import org.eclipse.equinox.internal.provisional.p2.artifact.repository.processing.ProcessingStepDescriptor; |
| import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException; |
| import org.eclipse.equinox.internal.provisional.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 metadata |
| * (e.g. table of contents files); |
| * |
| * This class is not used for reading or writing the actual artifacts. |
| */ |
| |
| // TODO: Should a registration/factory mechanism be supported |
| // for getting a repository reader/writer given a repository type |
| public class SimpleArtifactRepositoryIO { |
| |
| /** |
| * Writes the given artifact repository to the stream. |
| * This method performs buffering, and closes the stream when finished. |
| */ |
| public void write(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. |
| */ |
| public IArtifactRepository read(URL location, InputStream input, IProgressMonitor monitor) throws ProvisionException { |
| BufferedInputStream bufferedInput = null; |
| try { |
| try { |
| bufferedInput = new BufferedInputStream(input); |
| Parser repositoryParser = new Parser(Activator.getContext(), Activator.ID); |
| repositoryParser.parse(input); |
| IStatus result = repositoryParser.getStatus(); |
| switch (result.getSeverity()) { |
| case IStatus.CANCEL : |
| throw new OperationCanceledException(); |
| case IStatus.ERROR : |
| throw new ProvisionException(result); |
| case IStatus.WARNING : |
| case IStatus.INFO : |
| LogHelper.log(result); |
| } |
| return repositoryParser.getRepository(); |
| } finally { |
| if (bufferedInput != null) |
| bufferedInput.close(); |
| } |
| } catch (IOException ioe) { |
| String msg = NLS.bind(Messages.io_failedRead, location); |
| throw new ProvisionException(new Status(IStatus.ERROR, Activator.ID, ProvisionException.REPOSITORY_FAILED_READ, msg, ioe)); |
| } |
| } |
| |
| private interface XMLConstants extends org.eclipse.equinox.internal.p2.persistence.XMLConstants { |
| |
| // Constants defining the structure of the XML for a SimpleArtifactRepository |
| |
| // A format version number for simple artifact repository XML. |
| public static final Version CURRENT_VERSION = new Version(1, 0, 0); |
| public static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, new Version(2, 0, 0), false); |
| |
| // 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 REPOSITORY_PROPERTIES_ELEMENT = "repositoryProperties"; //$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$ |
| |
| 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_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? |
| |
| 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_CLASSIFIER_ATTRIBUTE, key.getClassifier()); |
| attribute(ID_ATTRIBUTE, key.getId()); |
| attribute(VERSION_ATTRIBUTE, key.getVersion()); |
| writeProcessingSteps(descriptor.getProcessingSteps()); |
| writeProperties(descriptor.getProperties()); |
| writeProperties(REPOSITORY_PROPERTIES_ELEMENT, descriptor.getRepositoryProperties()); |
| 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.equals(target)) { |
| // TODO: should the root handler be constructed based on class |
| // via an extension registry mechanism? |
| // String clazz = extractPIClass(data); |
| // TODO: version tolerance by extension |
| Version repositoryVersion = extractPIVersion(target, data); |
| if (!XML_TOLERANCE.isIncluded(repositoryVersion)) { |
| throw new SAXException(NLS.bind(Messages.io_incompatibleVersion, 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}; |
| |
| 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(); |
| } |
| |
| public void startElement(String name, Attributes attributes) { |
| if (MAPPING_RULES_ELEMENT.equals(name)) { |
| if (mappingRulesHandler == null) { |
| mappingRulesHandler = new MappingRulesHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else if (ARTIFACTS_ELEMENT.equals(name)) { |
| if (artifactsHandler == null) { |
| artifactsHandler = new ArtifactsHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else if (PROPERTIES_ELEMENT.equals(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()); |
| Map properties = (propertiesHandler == null ? new OrderedProperties(0) // |
| : propertiesHandler.getProperties()); |
| Set artifacts = (artifactsHandler == null ? new HashSet(0) // |
| : artifactsHandler.getArtifacts()); |
| repository = new SimpleArtifactRepository(attrValues[0], attrValues[1], attrValues[2], attrValues[3], // |
| attrValues[4], artifacts, mappingRules, properties); |
| } |
| } |
| } |
| |
| 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.equals(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.equals(ARTIFACT_ELEMENT)) { |
| new ArtifactHandler(this, attributes, artifacts); |
| } else { |
| invalidElement(name, attributes); |
| } |
| } |
| } |
| |
| protected class ArtifactHandler extends AbstractHandler { |
| |
| private final String[] required = new String[] {ARTIFACT_CLASSIFIER_ATTRIBUTE, ID_ATTRIBUTE, VERSION_ATTRIBUTE}; |
| |
| private Set artifacts; |
| ArtifactDescriptor currentArtifact = null; |
| |
| private PropertiesHandler propertiesHandler = null; |
| private PropertiesHandler repositoryPropertiesHandler = 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[2]); |
| // TODO: resolve access restriction on ArtifactKey construction |
| currentArtifact = new ArtifactDescriptor(new ArtifactKey(values[0], values[1], version)); |
| } |
| |
| public ArtifactDescriptor getArtifact() { |
| return currentArtifact; |
| } |
| |
| public void startElement(String name, Attributes attributes) { |
| if (PROCESSING_STEPS_ELEMENT.equals(name)) { |
| if (processingStepsHandler == null) { |
| processingStepsHandler = new ProcessingStepsHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else if (PROPERTIES_ELEMENT.equals(name)) { |
| if (propertiesHandler == null) { |
| propertiesHandler = new PropertiesHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else if (REPOSITORY_PROPERTIES_ELEMENT.equals(name)) { |
| if (repositoryPropertiesHandler == null) { |
| repositoryPropertiesHandler = new PropertiesHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else { |
| invalidElement(name, attributes); |
| } |
| } |
| |
| protected void finished() { |
| if (isValidXML() && currentArtifact != null) { |
| Map properties = (propertiesHandler == null ? new OrderedProperties(0) : propertiesHandler.getProperties()); |
| currentArtifact.addProperties(properties); |
| |
| properties = (repositoryPropertiesHandler == null ? new OrderedProperties(0) : repositoryPropertiesHandler.getProperties()); |
| currentArtifact.addRepositoryProperties(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.equals(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_REQUIRED_ATTRIBUTE}; |
| private final String[] optional = new String[] {STEP_DATA_ATTRIBUTE}; |
| |
| public ProcessingStepHandler(AbstractHandler parentHandler, Attributes attributes, List processingSteps) { |
| super(parentHandler, PROCESSING_STEP_ELEMENT); |
| String[] attributeValues = parseAttributes(attributes, required, optional); |
| 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.io_parseError; |
| } |
| |
| public String toString() { |
| // TODO: |
| return null; |
| } |
| |
| } |
| |
| } |