package aQute.maven;
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;
import javax.xml.stream.*;
import aQute.lib.converter.*;
import aQute.libg.reporter.*;
import aQute.maven.MavenRepository.PomImpl;
import aQute.struct.*;
/**
* Parse and XML pom.
*/
@SuppressWarnings({
"unchecked", "rawtypes"
})
class XmlCodec extends ReporterAdapter {
static XMLInputFactory xif = XMLInputFactory.newInstance();
static {
xif.setProperty(XMLInputFactory.IS_COALESCING, false);
xif.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
xif.setProperty(XMLInputFactory.IS_VALIDATING, false);
xif.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
}
boolean debug;
Map<String,String> properties = new HashMap<String,String>();
public XmlCodec(Map<String,String> macros) {
properties.putAll(macros);
}
public XmlCodec() {}
public PomImpl parse(Reader in) throws Exception {
try {
XMLStreamReader rd = xif.createXMLStreamReader(in);
try {
while (true) {
if (!rd.hasNext())
throw new IllegalArgumentException("Contains no content");
if (rd.next() == XMLStreamConstants.START_ELEMENT)
break;
}
PomImpl pom = (PomImpl) parseStruct(PomImpl.class, rd);
if (!rd.hasNext())
error("Malformed XML, found element after end: %s", rd.getLocalName());
pom.errors.addAll(getErrors());
pom.warnings.addAll(getWarnings());
return pom;
}
finally {
rd.close();
}
}
finally {
in.close();
}
}
/**
* a) String b) list c) object
*
* @param type
* @param rd
* @return
* @throws Exception
*/
private Object parse(Type type, XMLStreamReader rd) throws Exception {
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Class< ? > c = (Class< ? >) getRawType(pt);
if (Map.class.isAssignableFrom(c))
return parseMap(c, pt.getActualTypeArguments()[1], rd);
else if (Collection.class.isAssignableFrom(c))
return parseList(pt, rd);
else {
error("Parameterized type %s, only collection and map allowed at tag %s", type, rd.getLocalName());
}
}
if (struct.class.isAssignableFrom((Class< ? >) type)) {
return parseStruct((Class< ? extends struct>) type, rd);
}
// Only text left
StringBuilder sb = new StringBuilder();
while (rd.hasNext()) {
int nextTag = rd.next();
switch (nextTag) {
case XMLStreamConstants.CHARACTERS :
sb.append(rd.getText());
break;
case XMLStreamConstants.ENTITY_REFERENCE :
String entity = rd.getLocalName();
char replacement = Entities.getEntity(entity);
if (replacement != 0)
sb.append(replacement);
else
sb.append(entity);
break;
case XMLStreamConstants.START_ELEMENT :
error("Parsing %s, which is not complex, so unexpected element %s", type, rd.getLocalName());
break;
case XMLStreamConstants.END_ELEMENT :
if (sb.length() == 0)
return null;
return sb.toString().trim();
}
}
error("No more tags for %s and not found closing tag %s", type, rd.getLocalName());
return null;
}
private Class< ? > getRawType(Type t) {
if (t instanceof Class< ? >)
return (Class< ? >) t;
if (t instanceof ParameterizedType)
return getRawType(((ParameterizedType) t).getRawType());
if (t instanceof GenericArrayType) {
Class< ? > c = getRawType(((GenericArrayType) t).getGenericComponentType());
return Array.newInstance(c, 0).getClass();
}
if (t instanceof WildcardType) {
return getRawType(((WildcardType) t).getUpperBounds()[0]);
}
if (t instanceof TypeVariable< ? >) {
return getRawType(((TypeVariable) t).getBounds()[0]);
}
error("Cannot determine raw type of %s", t);
return null;
}
private Object parseMap(Class< ? > c, Type type, XMLStreamReader rd) throws Exception {
String enclosingTagName = rd.getLocalName();
Map<String,Object> map = new HashMap<String,Object>();
while (rd.hasNext()) {
int nextTag = rd.next();
switch (nextTag) {
case XMLStreamConstants.CHARACTERS :
if (!rd.getText().trim().isEmpty()) {
unexpectedText(enclosingTagName, rd.getText());
}
break;
case XMLStreamConstants.START_ELEMENT :
String name = rd.getLocalName();
Object value;
value = parseUndecided(rd);
map.put(name, value);
if (debug) {
System.out.println(name + "=" + value);
}
break;
case XMLStreamConstants.END_ELEMENT :
return map;
}
}
error("Unexpected eof for parsing map tag %s", enclosingTagName);
return null;
}
private Object parseList(ParameterizedType type, XMLStreamReader rd) throws Exception {
String enclosingTagName = rd.getLocalName();
Collection<Object> result = (Collection<Object>) Converter.cnv(type, new Object[0]);
while (rd.hasNext()) {
int nextTag = rd.next();
switch (nextTag) {
case XMLStreamConstants.CHARACTERS :
if (!rd.getText().trim().isEmpty())
unexpectedText(enclosingTagName, rd.getText());
break;
case XMLStreamConstants.START_ELEMENT :
// TODO verify that the same name is used?
// TODO verify that the actual name is the parent - the last
// s?
result.add(parse(type.getActualTypeArguments()[0], rd));
break;
case XMLStreamConstants.END_ELEMENT :
return result;
}
}
return null;
}
private struct parseStruct(Class< ? extends struct> type, XMLStreamReader rd) throws Exception {
struct object = type.newInstance();
while (rd.hasNext()) {
int nextTag = rd.next();
outer: switch (nextTag) {
case XMLStreamConstants.CHARACTERS :
if (!rd.getText().trim().isEmpty()) {
for (Field f : object.fields()) {
if (f.getType() == String.class) {
f.set(object, rd.getText());
break outer;
}
}
// Hmm, no string field
if (object.__extra == null) {
object.__extra = new HashMap<String,Object>();
}
String s = (String) object.__extra.get(".");
if (s == null) {
s = rd.getText();
} else
s += rd.getText();
object.__extra.put(".", s);
}
break;
case XMLStreamConstants.START_ELEMENT :
String name = rd.getLocalName();
Field field = object.getField(name);
Object value;
if (field == null) {
Map<String,Object> map = object.__extra;
if (map == null) {
map = new HashMap<String,Object>();
object.__extra = map;
}
map.put(name, value = parseUndecided(rd));
} else {
value = parse(field.getGenericType(), rd);
try {
if (field.getType() == URI.class) {
value = value.toString().replaceAll("\\$\\{([^}]+)\\}", "++++$1++++");
}
field.set(object, Converter.cnv(field.getGenericType(), value));
}
catch (Exception e) {
error("Cannot convert %s=%s to %s", name, value, field.getGenericType());
}
}
if (debug) {
System.out.println(name);
}
break;
case XMLStreamConstants.END_ELEMENT :
return object;
}
}
throw new IllegalArgumentException("Unexpected eof");
}
private Object parseUndecided(XMLStreamReader rd) throws Exception {
String enclosingTagName = rd.getLocalName();
Map<String,Object> map = null;
StringBuilder sb = new StringBuilder();
while (rd.hasNext()) {
int nextTag = rd.next();
switch (nextTag) {
case XMLStreamConstants.CHARACTERS :
if (!rd.getText().trim().isEmpty()) {
if (map != null)
unexpectedText(enclosingTagName, rd.getText());
sb.append(rd.getText());
}
break;
case XMLStreamConstants.START_ELEMENT :
String name = rd.getLocalName();
if (map == null)
map = new HashMap<String,Object>();
map.put(name, parseUndecided(rd));
if (debug) {
System.out.println(name);
}
break;
case XMLStreamConstants.END_ELEMENT :
if (map != null)
return map;
if (sb.length() > 0)
// TODO make trim method on sb
return sb.toString().trim();
return null;
}
}
error("Unexpected eof in %s )", enclosingTagName);
return null;
}
private void unexpectedText(String enclosingTag, String text) {
if (text == null)
return;
text = text.trim();
if (text.isEmpty())
return;
if (text.length() > 20)
text = text.substring(0, 10) + "..." + text.substring(text.length() - 10);
error("Parsing tag '%s'; Found text and members: '%s'", enclosingTag, text.trim());
}
public XmlCodec debug() {
this.debug = true;
return this;
}
}