aQute.bnd.osgi.repository.XMLResourceParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of biz.aQute.bndlib Show documentation
Show all versions of biz.aQute.bndlib Show documentation
bndlib: A Swiss Army Knife for OSGi
package aQute.bnd.osgi.repository;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_NAME;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_NAMESPACE;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_REFERRAL_DEPTH;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_REFERRAL_URL;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_REPOSITORY_INCREMENT;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_REPOSITORY_NAME;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_TYPE;
import static aQute.bnd.osgi.repository.XMLResourceConstants.ATTR_VALUE;
import static aQute.bnd.osgi.repository.XMLResourceConstants.NS_URI;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_ATTRIBUTE;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_CAPABILITY;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_DIRECTIVE;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_REFERRAL;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_REPOSITORY;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_REQUIREMENT;
import static aQute.bnd.osgi.repository.XMLResourceConstants.TAG_RESOURCE;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.osgi.resource.Resource;
import org.osgi.service.repository.ContentNamespace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aQute.bnd.header.Attrs;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.resource.CapReqBuilder;
import aQute.bnd.osgi.resource.ResourceBuilder;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;
import aQute.lib.xml.XML;
import aQute.libg.gzip.GZipUtils;
public class XMLResourceParser extends Processor {
private final static Logger logger = LoggerFactory.getLogger(XMLResourceParser.class);
final static XMLInputFactory inputFactory = XML.newXMLInputFactory();
static {
inputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, true);
inputFactory.setProperty(XMLInputFactory.IS_VALIDATING, false);
}
final private List resources = new ArrayList<>();
final private XMLStreamReader reader;
final private Set traversed;
final private String what;
final private URI url;
private int depth;
private String name;
private long increment;
public static List getResources(URI uri) throws Exception {
try (XMLResourceParser parser = new XMLResourceParser(uri)) {
return parser.parse();
}
}
public static List getResources(File file) throws Exception {
try (XMLResourceParser parser = new XMLResourceParser(file)) {
return parser.parse();
}
}
public static List getResources(File file, URI base) throws Exception {
try (XMLResourceParser parser = new XMLResourceParser(file, base)) {
return parser.parse();
}
}
public static List getResources(InputStream in, URI base) throws Exception {
try (XMLResourceParser parser = new XMLResourceParser(in, "parse", base)) {
return parser.parse();
}
}
public XMLResourceParser(URI url) throws Exception {
this(IO.stream(url.toURL()), url.toString(), url);
}
public XMLResourceParser(InputStream in, String what, URI uri) throws Exception {
this(in, what, 100, new HashSet<>(), uri);
}
public void setDepth(int n) {
this.depth = n;
}
public XMLResourceParser(InputStream in, String what, int depth, Set traversed, URI url) throws Exception {
this.what = what;
this.depth = depth;
this.traversed = traversed;
this.url = url;
in = GZipUtils.detectCompression(in);
addClose(in);
this.reader = inputFactory.createXMLStreamReader(in);
}
public XMLResourceParser(File location) throws Exception {
this(location, location.toURI());
}
public XMLResourceParser(File location, URI url) throws Exception {
this(IO.stream(location), location.getAbsolutePath(), url);
}
@Override
public void close() throws IOException {
try {
reader.close();
} catch (XMLStreamException e) {
throw new IOException(e);
} finally {
super.close();
}
}
public String name() {
return name;
}
public long increment() {
return increment;
}
List getResources() {
if (!isOk())
return null;
return resources;
}
public List parse() throws Exception {
if (!check(reader.hasNext(), "No content found"))
return null;
next();
if (!check(reader.isStartElement(), "Expected a start element at the root, is %s", reader.getEventType()))
return null;
String localName = reader.getLocalName();
if (!check(TAG_REPOSITORY.equals(localName), "Invalid tag name of top element, expected %s, got %s",
TAG_REPOSITORY, localName))
return null;
String nsUri = reader.getNamespaceURI();
if (nsUri != null) {
check(NS_URI.equals(nsUri), "Incorrect namespace. Expected %s, got %s", NS_URI, nsUri);
}
name = reader.getAttributeValue(null, ATTR_REPOSITORY_NAME);
String incrementString = reader.getAttributeValue(null, ATTR_REPOSITORY_INCREMENT);
if (incrementString != null) {
increment = Long.parseLong(incrementString);
}
next(); // either start resource/referral or end
while (reader.isStartElement()) {
localName = reader.getLocalName();
if (localName.equals(TAG_REFERRAL))
parseReferral();
else if (localName.equals(TAG_RESOURCE))
parseResource(resources);
else {
check(false, "Unexpected element %s", localName);
next();
}
}
check(reader.isEndElement() && reader.getLocalName()
.equals(TAG_REPOSITORY), "Expected to be at the end but are on %s", reader.getLocalName());
return getResources();
}
public void next() throws XMLStreamException {
report();
reader.nextTag();
}
private void report() {
int type = reader.getEventType();
switch (type) {
case XMLStreamConstants.START_DOCUMENT :
case XMLStreamConstants.START_ELEMENT :
case XMLStreamConstants.END_ELEMENT :
break;
default :
logger.debug("** unknown element, event type {}", type);
break;
}
}
private void parseReferral() throws Exception {
if (--depth < 0)
error("Too deep, traversed %s", traversed);
else {
String depthString = reader.getAttributeValue(null, ATTR_REFERRAL_DEPTH);
String urlString = reader.getAttributeValue(null, ATTR_REFERRAL_URL);
if (check(urlString != null, "Expected URL in referral")) {
// TODO resolve url
URI url = this.url.resolve(urlString);
traversed.add(url);
int depth = 100;
if (depthString != null) {
depth = Integer.parseInt(depthString);
}
InputStream in = url.toURL()
.openStream();
try (XMLResourceParser referralParser = new XMLResourceParser(in, urlString, depth, traversed, url)) {
referralParser.parse();
resources.addAll(referralParser.resources);
}
}
}
next();
tagEnd(TAG_REFERRAL);
}
private void tagEnd(String tag) throws XMLStreamException {
if (!check(reader.isEndElement(), "Expected end element, got %s for %s (%s)", reader.getEventType(), tag,
reader.getLocalName())) {
logger.debug("oops, invalid end {}", tag);
}
next();
}
private void parseResource(List resources) throws Exception {
ResourceBuilder resourceBuilder = new ResourceBuilder();
next();
while (reader.isStartElement()) {
parseCapabilityOrRequirement(resourceBuilder);
}
Resource resource = resourceBuilder.build();
resources.add(resource);
tagEnd(TAG_RESOURCE);
}
private void parseCapabilityOrRequirement(ResourceBuilder resourceBuilder) throws Exception {
String name = reader.getLocalName();
check(TAG_REQUIREMENT.equals(name) || TAG_CAPABILITY.equals(name), "Expected <%s> or <%s> tag, got <%s>",
TAG_REQUIREMENT, TAG_CAPABILITY, name);
String namespace = reader.getAttributeValue(null, ATTR_NAMESPACE);
CapReqBuilder capReqBuilder = new CapReqBuilder(namespace);
next();
while (reader.isStartElement()) {
parseAttributesOrDirectives(capReqBuilder);
}
if (TAG_REQUIREMENT.equals(name)) {
resourceBuilder.addRequirement(capReqBuilder);
} else {
resourceBuilder.addCapability(capReqBuilder);
}
tagEnd(name);
}
private void parseAttributesOrDirectives(CapReqBuilder capReqBuilder) throws Exception {
String name = reader.getLocalName();
switch (name) {
case TAG_ATTRIBUTE :
parseAttribute(capReqBuilder);
break;
case TAG_DIRECTIVE :
parseDirective(capReqBuilder);
break;
default :
check(false, "Invalid tag, expected either <%s> or <%s>, got <%s>", TAG_ATTRIBUTE, TAG_DIRECTIVE);
}
next();
tagEnd(name);
}
private boolean check(boolean check, String format, Object... args) {
if (check)
return true;
String message = Strings.format(format, args);
error("%s: %s", what, message);
return false;
}
private void parseAttribute(CapReqBuilder capReqBuilder) throws Exception {
String attributeName = reader.getAttributeValue(null, ATTR_NAME);
String attributeValue = reader.getAttributeValue(null, ATTR_VALUE);
String attributeType = reader.getAttributeValue(null, ATTR_TYPE);
if (isContent(capReqBuilder) && ContentNamespace.CAPABILITY_URL_ATTRIBUTE.equals(attributeName)) {
attributeValue = url.resolve(attributeValue)
.toString();
}
Object value = Attrs.convert(attributeType, attributeValue);
capReqBuilder.addAttribute(attributeName, value);
}
private boolean isContent(CapReqBuilder capReqBuilder) {
return ContentNamespace.CONTENT_NAMESPACE.equals(capReqBuilder.getNamespace());
}
private void parseDirective(CapReqBuilder capReqBuilder) throws XMLStreamException {
String attributeName = reader.getAttributeValue(null, ATTR_NAME);
String attributeValue = reader.getAttributeValue(null, ATTR_VALUE);
String attributeType = reader.getAttributeValue(null, ATTR_TYPE);
check(attributeType == null, "Expected a directive to have no type: %s:%s=%s", attributeName, attributeType,
attributeValue);
capReqBuilder.addDirective(attributeName, attributeValue);
}
}