org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorUpdater Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of driver-cql-shaded Show documentation
Show all versions of driver-cql-shaded Show documentation
A Shaded CQL ActivityType driver for http://nosqlbench.io/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.ivy.plugins.parser.xml;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.ivy.core.module.descriptor.Configuration;
import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.descriptor.ExtendsDescriptor;
import org.apache.ivy.core.module.descriptor.InheritableItem;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleId;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.plugins.namespace.NameSpaceHelper;
import org.apache.ivy.plugins.namespace.Namespace;
import org.apache.ivy.plugins.parser.ParserSettings;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.repository.file.FileResource;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.util.Checks;
import org.apache.ivy.util.DateUtil;
import org.apache.ivy.util.Message;
import org.apache.ivy.util.XMLHelper;
import org.apache.ivy.util.extendable.ExtendableItemHelper;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
/**
* Used to update ivy files. Uses ivy file as source and not ModuleDescriptor to preserve as much as
* possible the original syntax
*/
public final class XmlModuleDescriptorUpdater {
//CheckStyle:StaticVariableName| OFF
//LINE_SEPARATOR is actually a constant, but we have to modify it for the tests
public static String LINE_SEPARATOR = System.getProperty("line.separator");
//CheckStyle:StaticVariableName| ON
private XmlModuleDescriptorUpdater() {
}
/**
* used to copy a module descriptor xml file (also known as ivy file) and update the revisions
* of its dependencies, its status and revision
*
* @param srcURL
* the url of the source module descriptor file
* @param destFile
* The file to which the updated module descriptor should be output
*/
public static void update(URL srcURL, File destFile, UpdateOptions options)
throws IOException, SAXException {
if (destFile.getParentFile() != null) {
destFile.getParentFile().mkdirs();
}
OutputStream destStream = new FileOutputStream(destFile);
try {
update(srcURL, destStream, options);
} finally {
try {
destStream.close();
} catch (IOException e) {
Message.warn("failed to close a stream : " + e.toString());
}
}
}
public static void update(URL srcURL, OutputStream destFile, UpdateOptions options)
throws IOException, SAXException {
InputStream in = srcURL.openStream();
try {
update(srcURL, in, destFile, options);
} finally {
try {
in.close();
} catch (IOException e) {
Message.warn("failed to close a stream : " + e.toString());
}
try {
destFile.close();
} catch (IOException e) {
Message.warn("failed to close a stream : " + e.toString());
}
}
}
public static void update(InputStream in, Resource res,
File destFile, UpdateOptions options) throws IOException, SAXException {
if (destFile.getParentFile() != null) {
destFile.getParentFile().mkdirs();
}
OutputStream fos = new FileOutputStream(destFile);
try {
//TODO: use resource as input stream context?
URL inputStreamContext = null;
if (res instanceof URLResource) {
inputStreamContext = ((URLResource) res).getURL();
} else if (res instanceof FileResource) {
inputStreamContext = ((FileResource) res).getFile().toURI().toURL();
}
update(inputStreamContext, in, fos, options);
} finally {
try {
in.close();
} catch (IOException e) {
Message.warn("failed to close a stream : " + e.toString());
}
try {
fos.close();
} catch (IOException e) {
Message.warn("failed to close a stream : " + e.toString());
}
}
}
private static class UpdaterHandler extends DefaultHandler implements LexicalHandler {
/** standard attributes of ivy-module/info */
private static final Collection STD_ATTS = Arrays.asList(new String[] {"organisation",
"module", "branch", "revision", "status", "publication", "namespace"});
/** elements that may appear inside ivy-module, in expected order */
private static final List MODULE_ELEMENTS = Arrays.asList(new String[] {
"info", "configurations", "publications", "dependencies", "conflicts"
});
/** element position of "configurations" inside "ivy-module" */
private static final int CONFIGURATIONS_POSITION = MODULE_ELEMENTS.indexOf("configurations");
/** element position of "dependencies" inside "ivy-module" */
private static final int DEPENDENCIES_POSITION = MODULE_ELEMENTS.indexOf("dependencies");
/** elements that may appear inside of ivy-module/info */
private static final Collection INFO_ELEMENTS = Arrays.asList(new String[] {"extends",
"ivyauthor", "license", "repository", "description"});
private final ParserSettings settings;
private final PrintWriter out;
private final Map resolvedRevisions;
private final Map resolvedBranches;
private final String status;
private final String revision;
private final Date pubdate;
private final Namespace ns;
private final boolean replaceInclude;
private final boolean generateRevConstraint;
private boolean inHeader = true;
private final List confs;
private final URL relativePathCtx;
private final UpdateOptions options;
public UpdaterHandler(URL relativePathCtx, PrintWriter out, final UpdateOptions options) {
this.options = options;
this.settings = options.getSettings();
this.out = out;
this.resolvedRevisions = options.getResolvedRevisions();
this.resolvedBranches = options.getResolvedBranches();
this.status = options.getStatus();
this.revision = options.getRevision();
this.pubdate = options.getPubdate();
this.ns = options.getNamespace();
this.replaceInclude = options.isReplaceInclude();
this.generateRevConstraint = options.isGenerateRevConstraint();
this.relativePathCtx = relativePathCtx;
if (options.getConfsToExclude() != null) {
this.confs = Arrays.asList(options.getConfsToExclude());
} else {
this.confs = Collections.EMPTY_LIST;
}
}
// never print *ln* cause \n is found in copied characters stream
// nor do we need do handle indentation, original one is maintained except for attributes
private String organisation = null;
// defaultConfMapping of imported configurations, if any
private String defaultConfMapping = null;
// confMappingOverride of imported configurations, if any
private Boolean confMappingOverride = null;
// used to know if the last open tag was empty, to adjust termination
// with /> instead of >
private String justOpen = null;
//track the size of the left indent, so that inserted elements are formatted
//like nearby elements.
//true when we're reading indent whitespace
private boolean indenting;
private StringBuffer currentIndent = new StringBuffer();
private ArrayList indentLevels = new ArrayList(); // ArrayList
//true if an ivy-module/info/description element has been found in the published descriptor
private boolean hasDescription = false;
//true if merged configurations have been written
private boolean mergedConfigurations = false;
//true if merged deps have been written
private boolean mergedDependencies = false;
// the new value of the defaultconf attribute on the publications tag
private String newDefaultConf = null;
private Stack context = new Stack();
private Stack buffers = new Stack();
private Stack confAttributeBuffers = new Stack();
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
inHeader = false;
endIndent();
if (justOpen != null) {
write(">");
}
flushMergedElementsBefore(qName);
// according to ivy.xsd, all elements must occur before
// the , or elements
if (options.isMerge()
&& ("exclude".equals(localName)
|| "override".equals(localName)
|| "conflict".equals(localName))
&& "ivy-module/dependencies".equals(getContext())) {
ModuleDescriptor merged = options.getMergedDescriptor();
writeInheritedDependencies(merged);
out.println();
out.print(getIndent());
}
context.push(qName);
String path = getContext();
if ("info".equals(qName)) {
infoStarted(attributes);
} else if (replaceInclude && "include".equals(qName)
&& context.contains("configurations")) {
//TODO, in the case of !replaceInclude, we should still replace the relative path
//by an absolute path.
includeStarted(attributes);
} else if ("ivy-module/info/extends".equals(path)) {
startExtends(attributes);
} else if ("ivy-module/dependencies/dependency".equals(path)) {
startElementInDependency(attributes);
} else if ("dependencies".equals(qName)) {
startDependencies(attributes);
} else if ("ivy-module/configurations/conf".equals(path)) {
startElementInConfigurationsConf(qName, attributes);
} else if ("ivy-module/publications/artifact/conf".equals(path)
|| "ivy-module/dependencies/dependency/conf".equals(path)
|| "ivy-module/dependencies/dependency/artifact/conf".equals(path)) {
buffers.push(new ExtendedBuffer(getContext()));
((ExtendedBuffer) confAttributeBuffers.peek()).setDefaultPrint(false);
String confName = substitute(settings, attributes.getValue("name"));
if (!confs.contains(confName)) {
((ExtendedBuffer) confAttributeBuffers.peek()).setPrint(true);
((ExtendedBuffer) buffers.peek()).setPrint(true);
write("<" + qName);
for (int i = 0; i < attributes.getLength(); i++) {
write(" " + attributes.getQName(i) + "=\""
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
} else if ("ivy-module/publications/artifact".equals(path)) {
ExtendedBuffer buffer = new ExtendedBuffer(getContext());
buffers.push(buffer);
confAttributeBuffers.push(buffer);
write("<" + qName);
buffer.setDefaultPrint(attributes.getValue("conf") == null
&& ((newDefaultConf == null) || (newDefaultConf.length() > 0)));
for (int i = 0; i < attributes.getLength(); i++) {
String attName = attributes.getQName(i);
if ("conf".equals(attName)) {
String confName = substitute(settings, attributes.getValue("conf"));
String newConf = removeConfigurationsFromList(confName, confs);
if (newConf.length() > 0) {
write(" " + attributes.getQName(i) + "=\"" + newConf + "\"");
((ExtendedBuffer) buffers.peek()).setPrint(true);
}
} else {
write(" " + attributes.getQName(i) + "=\""
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
} else if ("ivy-module/dependencies/dependency/artifact".equals(path)) {
ExtendedBuffer buffer = new ExtendedBuffer(getContext());
buffers.push(buffer);
confAttributeBuffers.push(buffer);
write("<" + qName);
buffer.setDefaultPrint(attributes.getValue("conf") == null);
for (int i = 0; i < attributes.getLength(); i++) {
String attName = attributes.getQName(i);
if ("conf".equals(attName)) {
String confName = substitute(settings, attributes.getValue("conf"));
String newConf = removeConfigurationsFromList(confName, confs);
if (newConf.length() > 0) {
write(" " + attributes.getQName(i) + "=\"" + newConf + "\"");
((ExtendedBuffer) buffers.peek()).setPrint(true);
}
} else {
write(" " + attributes.getQName(i) + "=\""
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
} else if ("ivy-module/publications".equals(path)) {
startPublications(attributes);
} else {
if (options.isMerge() && path.startsWith("ivy-module/info")) {
ModuleDescriptor merged = options.getMergedDescriptor();
if (path.equals("ivy-module/info/description")) {
//if the descriptor already contains a description, don't bother printing
//the merged version.
hasDescription = true;
} else if (!INFO_ELEMENTS.contains(qName)) {
//according to the XSD, we should write description after all of the other
//standard elements but before any extended elements.
writeInheritedDescription(merged);
}
}
// copy
write("<" + qName);
for (int i = 0; i < attributes.getLength(); i++) {
write(" " + attributes.getQName(i) + "=\""
+ substitute(settings, attributes.getValue(i)) + "\"");
}
}
justOpen = qName;
// indent.append("\t");
}
private void startExtends(Attributes attributes) {
// in merge mode, comment out extends element
if (options.isMerge()) {
write("");
}
/**
* Collect the given list of inherited descriptor items into lists keyed by parent Id.
* Thus all of the items inherited from parent A can be written together, then all of
* the items from parent B, and so on.
* @param merged the merged child descriptor
* @param items the inherited items to collate
* @return maps parent ModuleRevisionId to a List of InheritedItems imported from that parent
*/
private Map/**/ collateInheritedItems(ModuleDescriptor merged,
InheritableItem[] items) {
LinkedHashMap/**/ inheritedItems = new LinkedHashMap();
for (int i = 0; i < items.length; ++i) {
ModuleRevisionId source = items[i].getSourceModule();
//ignore items that are defined directly in the child descriptor
if (source != null
&& !source.getModuleId().equals(merged.getModuleRevisionId().getModuleId())) {
List accum = (List) inheritedItems.get(source);
if (accum == null) {
accum = new ArrayList();
inheritedItems.put(source, accum);
}
accum.add(items[i]);
}
}
return inheritedItems;
}
/**
* If no info/description element has yet been written, write the description inherited from
* the parent descriptor, if any. Calling this method more than once has no affect.
*/
private void writeInheritedDescription(ModuleDescriptor merged) {
if (!hasDescription) {
hasDescription = true;
String description = merged.getDescription();
if (description != null) {
PrintWriter writer = getWriter();
if (justOpen != null) {
writer.println(">");
}
writeInheritanceComment("description", "parent");
writer.println(getIndent() + "" + XMLHelper.escape(description) + " ");
//restore the indent that existed before we wrote the extra elements
writer.print(currentIndent);
justOpen = null;
}
}
}
private void writeInheritedConfigurations(ModuleDescriptor merged) {
if (!mergedConfigurations) {
mergedConfigurations = true;
writeInheritedItems(merged, merged.getConfigurations(),
ConfigurationPrinter.INSTANCE, "configurations", false);
}
}
private void writeInheritedDependencies(ModuleDescriptor merged) {
if (!mergedDependencies) {
mergedDependencies = true;
writeInheritedItems(merged, merged.getDependencies(),
DependencyPrinter.INSTANCE, "dependencies", false);
}
}
/**
* If publishing in merge mode, guarantee that any merged elements appearing
* before moduleElement
have been written. This method should
* be called before we write the start tag of moduleElement
.
* This covers cases where merged elements like "configurations" and "dependencies" appear
* in the parent descriptor, but are completely missing in the child descriptor.
*
* For example, if "moduleElement" is "dependencies", guarantees that "configurations"
* has been written. If moduleElement
is null
, then all
* missing merged elements will be flushed.
*
* @param moduleElement a descriptor element name, for example "configurations" or "info"
*/
private void flushMergedElementsBefore(String moduleElement) {
if (options.isMerge() && context.size() == 1 && "ivy-module".equals(context.peek())
&& !(mergedConfigurations && mergedDependencies)) {
//calculate the position of the element in ivy-module
int position = moduleElement == null ? MODULE_ELEMENTS.size()
: MODULE_ELEMENTS.indexOf(moduleElement);
ModuleDescriptor merged = options.getMergedDescriptor();
//see if we should write
if (!mergedConfigurations && position > CONFIGURATIONS_POSITION
&& merged.getConfigurations().length > 0) {
mergedConfigurations = true;
writeInheritedItems(merged, merged.getConfigurations(),
ConfigurationPrinter.INSTANCE, "configurations", true);
}
//see if we should write
if (!mergedDependencies && position > DEPENDENCIES_POSITION
&& merged.getDependencies().length > 0) {
mergedDependencies = true;
writeInheritedItems(merged, merged.getDependencies(),
DependencyPrinter.INSTANCE, "dependencies", true);
}
}
}
private void flushAllMergedElements() {
flushMergedElementsBefore(null);
}
public void endElement(String uri, String localName, String qName) throws SAXException {
String path = getContext();
if (options.isMerge()) {
ModuleDescriptor merged = options.getMergedDescriptor();
if ("ivy-module/info".equals(path)) {
//guarantee that inherited description has been written before
//info element closes.
writeInheritedDescription(merged);
} else if ("ivy-module/configurations".equals(path)) {
//write inherited configurations after all child configurations
writeInheritedConfigurations(merged);
} else if ("ivy-module/dependencies".equals(path)) {
//write inherited dependencies after all child dependencies
writeInheritedDependencies(merged);
} else if ("ivy-module".equals(path)) {
//write any remaining inherited data before we close the
//descriptor.
flushAllMergedElements();
}
}
if (qName.equals(justOpen)) {
write("/>");
} else {
write("" + qName + ">");
}
if (!buffers.isEmpty()) {
ExtendedBuffer buffer = (ExtendedBuffer) buffers.peek();
if (buffer.getContext().equals(path)) {
buffers.pop();
if (buffer.isPrint()) {
write(buffer.toString());
}
}
}
if (!confAttributeBuffers.isEmpty()) {
ExtendedBuffer buffer = (ExtendedBuffer) confAttributeBuffers.peek();
if (buffer.getContext().equals(path)) {
confAttributeBuffers.pop();
}
}
// element is commented out when in merge mode.
if (options.isMerge() && "ivy-module/info/extends".equals(path)) {
write(" -->");
}
justOpen = null;
context.pop();
}
public void endDocument() throws SAXException {
out.print(LINE_SEPARATOR);
out.flush();
out.close();
}
public void processingInstruction(String target, String data) throws SAXException {
write("");
write(target);
write(" ");
write(data);
write("?>");
write(LINE_SEPARATOR);
}
public void warning(SAXParseException e) throws SAXException {
throw e;
}
public void error(SAXParseException e) throws SAXException {
throw e;
}
public void fatalError(SAXParseException e) throws SAXException {
throw e;
}
public void endCDATA() throws SAXException {
}
public void endDTD() throws SAXException {
}
public void startCDATA() throws SAXException {
}
public void comment(char[] ch, int start, int length) throws SAXException {
if (justOpen != null) {
write(">");
justOpen = null;
}
StringBuffer comment = new StringBuffer();
comment.append(ch, start, length);
write("");
if (inHeader) {
write(LINE_SEPARATOR);
}
}
public void endEntity(String name) throws SAXException {
}
public void startEntity(String name) throws SAXException {
}
public void startDTD(String name, String publicId, String systemId) throws SAXException {
}
}
public static void update(URL inStreamCtx, InputStream inStream,
OutputStream outStream, final UpdateOptions options)
throws IOException, SAXException {
final PrintWriter out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8"));
out.write("");
out.write(LINE_SEPARATOR);
try {
UpdaterHandler updaterHandler = new UpdaterHandler(inStreamCtx, out, options);
InputSource inSrc = new InputSource(new BufferedInputStream(inStream));
if (inStreamCtx != null) {
inSrc.setSystemId(inStreamCtx.toExternalForm());
}
XMLHelper.parse(inSrc, null, updaterHandler, updaterHandler);
} catch (ParserConfigurationException e) {
IllegalStateException ise = new IllegalStateException(
"impossible to update Ivy files: parser problem");
ise.initCause(e);
throw ise;
}
}
private static class ExtendedBuffer {
private String context = null;
private Boolean print = null;
private boolean defaultPrint = false;
private StringWriter buffer = new StringWriter();
private PrintWriter writer = new PrintWriter(buffer);
ExtendedBuffer(String context) {
this.context = context;
}
boolean isPrint() {
if (print == null) {
return defaultPrint;
}
return print.booleanValue();
}
void setPrint(boolean print) {
this.print = Boolean.valueOf(print);
}
void setDefaultPrint(boolean print) {
this.defaultPrint = print;
}
PrintWriter getWriter() {
return writer;
}
String getContext() {
return context;
}
public String toString() {
writer.flush();
return buffer.toString();
}
}
/**
* Prints a descriptor item's XML representation
*/
protected static interface ItemPrinter {
/**
* Print an XML representation of item
to out
.
* @param parent the module descriptor containing item
* @param item subcomponent of the descriptor, for example a {@link DependencyDescriptor}
* or {@link Configuration}
*/
public void print(ModuleDescriptor parent, Object item, PrintWriter out);
}
protected static class DependencyPrinter implements ItemPrinter {
public static final DependencyPrinter INSTANCE = new DependencyPrinter();
public void print(ModuleDescriptor parent, Object item, PrintWriter out) {
XmlModuleDescriptorWriter.printDependency(parent, (DependencyDescriptor) item, out);
}
}
protected static class ConfigurationPrinter implements ItemPrinter {
public static final ConfigurationPrinter INSTANCE = new ConfigurationPrinter();
public void print(ModuleDescriptor parent, Object item, PrintWriter out) {
XmlModuleDescriptorWriter.printConfiguration((Configuration) item, out);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy