| /******************************************************************************* |
| * Copyright (c) 2008, 2011 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.persistence; |
| |
| import java.io.*; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.List; |
| import javax.xml.parsers.ParserConfigurationException; |
| import org.eclipse.equinox.internal.p2.core.helpers.OrderedProperties; |
| import org.eclipse.equinox.p2.metadata.Version; |
| import org.eclipse.equinox.p2.metadata.VersionRange; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.framework.BundleContext; |
| import org.xml.sax.*; |
| |
| /* |
| * Class used to read a composite repository. |
| */ |
| public class CompositeParser extends XMLParser implements XMLConstants { |
| |
| private static final Version CURRENT_VERSION = Version.createOSGi(1, 0, 0); |
| static final VersionRange XML_TOLERANCE = new VersionRange(CURRENT_VERSION, true, Version.createOSGi(2, 0, 0), false); |
| private static final String REQUIRED_CAPABILITY_ELEMENT = "required"; //$NON-NLS-1$ |
| private static final String REPOSITORY_ELEMENT = "repository"; //$NON-NLS-1$ |
| String repositoryType; |
| private CompositeRepositoryState theState; |
| |
| protected class ChildrenHandler extends AbstractHandler { |
| private ArrayList<URI> children; |
| |
| public ChildrenHandler(AbstractHandler parentHandler, Attributes attributes) { |
| super(parentHandler, CHILDREN_ELEMENT); |
| String size = parseOptionalAttribute(attributes, COLLECTION_SIZE_ATTRIBUTE); |
| children = (size != null ? new ArrayList<URI>(new Integer(size).intValue()) : new ArrayList<URI>(4)); |
| } |
| |
| public URI[] getChildren() { |
| return children.toArray(new URI[children.size()]); |
| } |
| |
| public void startElement(String name, Attributes attributes) { |
| if (name.equals(CHILD_ELEMENT)) { |
| new ChildHandler(this, attributes, children); |
| } else { |
| invalidElement(name, attributes); |
| } |
| } |
| } |
| |
| protected class ChildHandler extends AbstractHandler { |
| private final String[] required = new String[] {LOCATION_ELEMENT}; |
| private final String[] optional = new String[] {}; |
| |
| URI currentRepo = null; |
| |
| private List<URI> repos; |
| |
| public ChildHandler(AbstractHandler parentHandler, Attributes attributes, List<URI> repos) { |
| super(parentHandler, CHILD_ELEMENT); |
| String[] values = parseAttributes(attributes, required, optional); |
| this.repos = repos; |
| //skip entire subrepository if the location is missing |
| if (values[0] == null) |
| return; |
| currentRepo = checkURI(REQUIRED_CAPABILITY_ELEMENT, URI_ATTRIBUTE, values[0]); |
| |
| } |
| |
| public void startElement(String name, Attributes attributes) { |
| checkCancel(); |
| } |
| |
| protected void finished() { |
| if (currentRepo != null) |
| repos.add(currentRepo); |
| } |
| } |
| |
| 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 (repositoryType.equals(target)) { |
| Version repositoryVersion = extractPIVersion(target, data); |
| if (!XML_TOLERANCE.isIncluded(repositoryVersion)) { |
| throw new SAXException(NLS.bind(Messages.io_IncompatibleVersion, repositoryVersion, XML_TOLERANCE)); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Handler for the "repository" attribute. |
| */ |
| 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 PropertiesHandler propertiesHandler = null; |
| private ChildrenHandler childrenHandler = null; |
| private CompositeRepositoryState state; |
| private String[] attrValues = new String[required.length + optional.length]; |
| |
| public RepositoryHandler() { |
| super(); |
| } |
| |
| public CompositeRepositoryState getRepository() { |
| return state; |
| } |
| |
| 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 (PROPERTIES_ELEMENT.equals(name)) { |
| if (propertiesHandler == null) { |
| propertiesHandler = new PropertiesHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else if (CHILDREN_ELEMENT.equals(name)) { |
| if (childrenHandler == null) { |
| childrenHandler = new ChildrenHandler(this, attributes); |
| } else { |
| duplicateElement(this, name, attributes); |
| } |
| } else { |
| invalidElement(name, attributes); |
| } |
| } |
| |
| /* |
| * If we parsed valid XML then fill in our repository state object with the parsed data. |
| */ |
| protected void finished() { |
| if (isValidXML()) { |
| state = new CompositeRepositoryState(); |
| state.setName(attrValues[0]); |
| state.setType(attrValues[1]); |
| state.setVersion(attrValues[2]); |
| state.setDescription(attrValues[3]); |
| state.setProvider(attrValues[4]); |
| state.setProperties((propertiesHandler == null ? new OrderedProperties(0) // |
| : propertiesHandler.getProperties())); |
| state.setChildren((childrenHandler == null ? new URI[0] // |
| : childrenHandler.getChildren())); |
| } |
| } |
| } |
| |
| public CompositeParser(BundleContext context, String bundleId, String type) { |
| super(context, bundleId); |
| this.repositoryType = type; |
| } |
| |
| public void parse(File file) throws IOException { |
| // don't overwrite if we already have a filename/location |
| if (errorContext == null) |
| setErrorContext(file.getAbsolutePath()); |
| 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()) { |
| theState = repositoryHandler.getRepository(); |
| } |
| } catch (SAXException e) { |
| throw new IOException(e.getMessage()); |
| } catch (ParserConfigurationException e) { |
| throw new IOException(e.getMessage()); |
| } finally { |
| stream.close(); |
| } |
| } |
| |
| public CompositeRepositoryState getRepositoryState() { |
| return theState; |
| } |
| |
| //TODO what? |
| protected Object getRootObject() { |
| return null; |
| } |
| |
| protected String getErrorMessage() { |
| return Messages.io_parseError; |
| } |
| |
| } |