| /* |
| * Copyright (c) 2014-2016 Eike Stepper (Loehne, Germany) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.oomph.internal.version; |
| |
| import org.eclipse.oomph.util.IOUtil; |
| import org.eclipse.oomph.util.StringUtil; |
| import org.eclipse.oomph.version.IElement; |
| import org.eclipse.oomph.version.IRelease; |
| import org.eclipse.oomph.version.Markers; |
| import org.eclipse.oomph.version.VersionUtil; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| |
| import org.osgi.framework.Version; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.SAXParseException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| import javax.xml.parsers.SAXParser; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.NoSuchAlgorithmException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class Release implements IRelease |
| { |
| public static final String RELEASE_TAG = "release"; //$NON-NLS-1$ |
| |
| public static final String FEATURE_TAG = "feature"; //$NON-NLS-1$ |
| |
| public static final String PLUGIN_TAG = "plugin"; //$NON-NLS-1$ |
| |
| public static final String PRODUCT_TAG = "product"; //$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 FRAGMENT_ATTRIBUTE = "fragment"; //$NON-NLS-1$ |
| |
| public static final String LICENSE_ATTRIBUTE = "license"; //$NON-NLS-1$ |
| |
| private static final String INDENT = "\t"; //$NON-NLS-1$ |
| |
| private IFile file; |
| |
| private byte[] digest; |
| |
| private Map<IElement, IElement> elements = new HashMap<IElement, IElement>(); |
| |
| public Release(IFile file) |
| { |
| this.file = file; |
| } |
| |
| public Release(SAXParser parser, IFile file) throws CoreException, IOException, SAXException, NoSuchAlgorithmException |
| { |
| this.file = file; |
| |
| XMLHandler handler = new XMLHandler(); |
| InputStream contents = null; |
| |
| try |
| { |
| contents = file.getContents(); |
| parser.parse(contents, handler); |
| |
| if (!handler.hasReleaseTag()) |
| { |
| throw new IOException("Release specification file does not contain a <release> element: " + file.getFullPath()); //$NON-NLS-1$ |
| } |
| |
| digest = VersionUtil.getSHA1(file); |
| } |
| finally |
| { |
| IOUtil.closeSilent(contents); |
| } |
| } |
| |
| public IFile getFile() |
| { |
| return file; |
| } |
| |
| public byte[] getDigest() |
| { |
| return digest; |
| } |
| |
| public Map<IElement, IElement> getElements() |
| { |
| return elements; |
| } |
| |
| public IElement resolveElement(IElement key) |
| { |
| return elements.get(key); |
| } |
| |
| public int getSize() |
| { |
| return elements.size(); |
| } |
| |
| public void write() throws IOException, CoreException, NoSuchAlgorithmException |
| { |
| StringBuilder builder = new StringBuilder(); |
| writeRelease(builder); |
| |
| String xml = builder.toString(); |
| xml = xml.replace("\n", VersionUtil.getLineDelimiter(file)); //$NON-NLS-1$ |
| ByteArrayInputStream contents = new ByteArrayInputStream(xml.getBytes("UTF-8")); //$NON-NLS-1$ |
| if (file.exists()) |
| { |
| file.setContents(contents, true, true, new NullProgressMonitor()); |
| } |
| else |
| { |
| file.create(contents, true, new NullProgressMonitor()); |
| } |
| |
| digest = VersionUtil.getSHA1(file); |
| } |
| |
| private void writeRelease(StringBuilder builder) |
| { |
| builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); //$NON-NLS-1$ |
| builder.append("<" + RELEASE_TAG + ">\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| List<IElement> list = new ArrayList<IElement>(elements.keySet()); |
| Collections.sort(list, new Comparator<IElement>() |
| { |
| public int compare(IElement o1, IElement o2) |
| { |
| int result = o1.getType().compareTo(o2.getType()); |
| if (result == 0) |
| { |
| result = StringUtil.safe(o1.getName()).compareTo(StringUtil.safe(o2.getName())); |
| } |
| |
| if (result == 0) |
| { |
| result = o1.getVersion().compareTo(o2.getVersion()); |
| } |
| |
| return result; |
| } |
| }); |
| |
| for (IElement element : list) |
| { |
| writeElement(builder, element, INDENT); |
| } |
| |
| builder.append("</" + RELEASE_TAG + ">\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private void writeElement(StringBuilder builder, IElement element, String indent) |
| { |
| String name = element.getName(); |
| Version version = element.getVersion(); |
| |
| builder |
| .append(indent + "<" + element.getTag() + " " + NAME_ATTRIBUTE + "=\"" + name + "\" " + (element.isFragment() ? FRAGMENT_ATTRIBUTE + "=\"true\" " : "") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ |
| + VERSION_ATTRIBUTE + "=\"" + version + "\"" + (element.isLicenseFeature() ? " license=\"true\"" : "")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| |
| List<IElement> content = element.getChildren(); |
| if (content.isEmpty()) |
| { |
| builder.append("/"); //$NON-NLS-1$ |
| writeElementEnd(builder, element); |
| } |
| else |
| { |
| writeElementEnd(builder, element); |
| |
| for (IElement child : content) |
| { |
| writeElement(builder, child, indent + INDENT); |
| } |
| |
| builder.append(indent + "</" + element.getTag() + ">\n"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| private void writeElementEnd(StringBuilder builder, IElement element) |
| { |
| builder.append(">"); //$NON-NLS-1$ |
| if (element.getVersion().equals(Version.emptyVersion)) |
| { |
| builder.append(" <!-- UNRESOLVED -->"); //$NON-NLS-1$ |
| } |
| |
| builder.append("\n"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public class XMLHandler extends DefaultHandler |
| { |
| private IElement parent; |
| |
| private int level; |
| |
| public XMLHandler() |
| { |
| } |
| |
| public boolean hasReleaseTag() |
| { |
| return level == 1; |
| } |
| |
| @Override |
| public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException |
| { |
| if (RELEASE_TAG.equalsIgnoreCase(qName)) |
| { |
| ++level; |
| } |
| else if (FEATURE_TAG.equalsIgnoreCase(qName)) |
| { |
| if (level == 0) |
| { |
| return; |
| } |
| |
| IElement element = createElement(IElement.Type.FEATURE, attributes); |
| if (++level == 2) |
| { |
| elements.put(element, element); |
| parent = element; |
| } |
| else |
| { |
| parent.getChildren().add(element); |
| } |
| } |
| else if (PRODUCT_TAG.equalsIgnoreCase(qName)) |
| { |
| if (level == 0) |
| { |
| return; |
| } |
| |
| IElement element = createElement(IElement.Type.PRODUCT, attributes); |
| if (++level == 2) |
| { |
| elements.put(element, element); |
| parent = element; |
| } |
| else |
| { |
| parent.getChildren().add(element); |
| } |
| } |
| else if (PLUGIN_TAG.equalsIgnoreCase(qName)) |
| { |
| if (level == 0) |
| { |
| return; |
| } |
| |
| IElement element = createElement(IElement.Type.PLUGIN, attributes); |
| if (++level == 2) |
| { |
| elements.put(element, element); |
| parent = element; |
| } |
| else |
| { |
| parent.getChildren().add(element); |
| } |
| } |
| } |
| |
| private IElement createElement(IElement.Type type, Attributes attributes) throws SAXException |
| { |
| String name = getString(attributes, NAME_ATTRIBUTE); |
| Version version = new Version(getString(attributes, VERSION_ATTRIBUTE)); |
| boolean isFragment = "true".equals(getString(attributes, FRAGMENT_ATTRIBUTE)); //$NON-NLS-1$ |
| Element element = new Element(type, name, version, isFragment); |
| |
| String license = getString(attributes, LICENSE_ATTRIBUTE); |
| if ("true".equals(license)) //$NON-NLS-1$ |
| { |
| element.setLicenseFeature(true); |
| } |
| |
| return element; |
| } |
| |
| @Override |
| public void endElement(String uri, String localName, String qName) throws SAXException |
| { |
| if (FEATURE_TAG.equalsIgnoreCase(qName) || PLUGIN_TAG.equalsIgnoreCase(qName) || PRODUCT_TAG.equalsIgnoreCase(qName)) |
| { |
| --level; |
| } |
| } |
| |
| private String getString(Attributes attributes, String name) throws SAXException |
| { |
| String value = attributes.getValue(name); |
| if (value != null || LICENSE_ATTRIBUTE.equals(name) || FRAGMENT_ATTRIBUTE.equals(name)) |
| { |
| return value; |
| } |
| |
| throw new SAXException("Illegal value for " + name); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public void error(SAXParseException exception) throws SAXException |
| { |
| addMarker(exception, IMarker.SEVERITY_ERROR); |
| } |
| |
| @Override |
| public void fatalError(SAXParseException exception) throws SAXException |
| { |
| addMarker(exception, IMarker.SEVERITY_ERROR); |
| } |
| |
| @Override |
| public void warning(SAXParseException exception) throws SAXException |
| { |
| addMarker(exception, IMarker.SEVERITY_WARNING); |
| } |
| |
| private void addMarker(SAXParseException e, int severity) |
| { |
| try |
| { |
| Markers.addMarker(file, e.getMessage(), severity, e.getLineNumber()); |
| } |
| catch (Exception ex) |
| { |
| Activator.log(ex); |
| } |
| } |
| } |
| } |