I am developing an Android
app that should convert a valid Alert
string (i.e.: in XML
string format) to CAP Alert
object. To do so, I am using this Google-Cap-Library
.
Here is my code:
import android.app.Activity;
import android.os.Bundle;
import com.google.publicalerts.cap.AlertOrBuilder;
import com.google.publicalerts.cap.CapException;
import com.google.publicalerts.cap.CapValidator;
import com.google.publicalerts.cap.CapXmlParser;
import org.xml.sax.SAXParseException;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createAlertFromValidXml(new CapXmlParser(true));
}
public AlertOrBuilder createAlertFromValidXml(CapXmlParser parser) {
AlertOrBuilder alert = null;
try {
alert = parser.parseFrom(getValidAlertXmlString());
} catch (CapException e) {
e.printStackTrace();
} catch (SAXParseException e) {
e.printStackTrace();
}
return alert;
}
private String getValidAlertXmlString() {
return "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
+ "<alert xmlns=\"" + CapValidator.CAP_LATEST_XMLNS + "\">\n"
+ "<identifier>43b080713727</identifier>\n"
+ "<sender>[email protected]</sender>\n"
+ "<sent>2003-04-02T14:39:01-05:00</sent>\n"
+ "<status>Actual</status>\n"
+ "<msgType>Alert</msgType>\n"
+ "<source>a source</source>\n"
+ "<scope>Private</scope>\n"
+ "<addresses>\"address 1\" address2</addresses>\n"
+ "<code>abcde</code>\n"
+ "<code>fghij</code>\n"
+ "<note>a note</note>\n"
+ "<references>a,b,2002-04-02T14:39:01-05:00 </references>\n"
+ "<info>\n"
+ "<category>Security</category>\n"
+ "<category>Safety</category>\n"
+ "<event>Homeland Security Advisory System Update</event>\n"
+ "<urgency>Unknown</urgency>\n"
+ "<severity>Unknown</severity>\n"
+ "<certainty>Unknown</certainty>\n"
+ "<senderName>Department of Homeland Security</senderName>\n"
+ "<headline>Homeland Security Sets Code ORANGE</headline>\n"
+ "<description>DHS has set the threat level to ORANGE.</description>\n"
+ "<instruction>Take Protective Measures.</instruction>\n"
+ "<web>http://www.dhs.gov/dhspublic/display?theme=29</web>\n"
+ "</info>\n"
+ "<info>\n"
+ "<language>fr-CA</language>\n"
+ "<category>Safety</category>\n"
+ "<event>Homeland Security Advisory System Update</event>\n"
+ "<responseType>Evacuate</responseType>\n"
+ "<responseType>Shelter</responseType>\n"
+ "<urgency>Unknown</urgency>\n"
+ "<severity>Unknown</severity>\n"
+ "<certainty>Unknown</certainty>\n"
+ "<audience>an audience</audience>\n"
+ "<eventCode><valueName>EC</valueName><value>v1</value></eventCode>\n"
+ "<eventCode><valueName>EC2</valueName><value>v2</value></eventCode>\n"
+ "<effective>2003-04-02T14:39:01-05:00</effective>\n"
+ "<onset>2003-04-02T15:39:01+05:00</onset>\n"
+ "<expires>2003-04-02T16:39:01-00:00</expires>\n"
+ "<senderName>Department of Homeland Security</senderName>\n"
+ "<contact>a contact</contact>\n"
+ "<parameter>\n"
+ "<valueName>HSAS</valueName>\n"
+ "<value>ORANGE</value>\n"
+ "</parameter>\n"
+ "<parameter>\n"
+ "<valueName>p2</valueName>\n"
+ "<value>v2</value>\n"
+ "</parameter>\n"
+ "</info>\n"
+ "</alert>";
}
public String getXmlValues() {
return "Test";
}
}
However, when I run the app, it crashes and the logcat on Android-Studio shows me the following output:
UPDATE: CapXmlParser.java
package com.google.publicalerts.cap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import com.google.protobuf.ByteString;
import com.google.protobuf.MessageOrBuilder;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.Type;
import com.google.publicalerts.cap.Alert;
import com.google.publicalerts.cap.CachedSaxInputSource;
import com.google.publicalerts.cap.CapException;
import com.google.publicalerts.cap.CapUtil;
import com.google.publicalerts.cap.CapValidator;
import com.google.publicalerts.cap.Circle;
import com.google.publicalerts.cap.Group;
import com.google.publicalerts.cap.NotCapException;
import com.google.publicalerts.cap.Point;
import com.google.publicalerts.cap.Polygon;
import com.google.publicalerts.cap.Reason;
import com.google.publicalerts.cap.Reasons;
import com.google.publicalerts.cap.ValuePair;
import com.google.publicalerts.cap.XPath;
import com.google.publicalerts.cap.XmlUtil;
import com.google.publicalerts.cap.CapException.ReasonType;
import com.google.publicalerts.cap.Reason.Level;
import java.io.IOException;
import java.io.Reader;
import java.util.Map;
import java.util.Stack;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
public class CapXmlParser {
private static final Map<String, Schema> EXTENDED_SCHEMA_MAP = initSchemaMap(new String[]{"cap10_extended.xsd", "cap11_extended.xsd", "cap12_extended.xsd"});
private static final Map<String, Schema> STRICT_SCHEMA_MAP = initSchemaMap(new String[]{"cap10.xsd", "cap11.xsd", "cap12.xsd"});
private final boolean validate;
private final Map<String, Schema> schemaMap;
private static Map<String, Schema> initSchemaMap(String[] xsds) {
SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
String[] xmlns = new String[]{"http://www.incident.com/cap/1.0", "urn:oasis:names:tc:emergency:cap:1.1", "urn:oasis:names:tc:emergency:cap:1.2"};
Builder schemas = ImmutableMap.builder();
for(int i = 0; i < xsds.length; ++i) {
StreamSource cap = new StreamSource(CapXmlParser.class.getResourceAsStream("schema/" + xsds[i]));
try {
schemas.put(xmlns[i], schemaFactory.newSchema(new Source[]{cap}));
} catch (SAXException var7) {
throw new RuntimeException(var7);
}
}
return schemas.build();
}
public CapXmlParser(boolean validate) {
this(validate, false);
}
public CapXmlParser(boolean validate, boolean strictXsdValidation) {
this.validate = validate;
this.schemaMap = strictXsdValidation?STRICT_SCHEMA_MAP:EXTENDED_SCHEMA_MAP;
}
public static Circle toCircle(String str) {
if(str == null) {
return null;
} else {
String[] pointRadius = str.trim().split("\\s+");
if(pointRadius.length == 0) {
return null;
} else if(pointRadius.length != 2) {
return null;
} else {
Point point = toPoint(pointRadius[0]);
if(point == null) {
return null;
} else {
float radius;
try {
radius = Float.parseFloat(pointRadius[1]);
} catch (NumberFormatException var5) {
return null;
}
return Circle.newBuilder().setPoint(point).setRadius((double)radius).buildPartial();
}
}
}
}
static Point toPoint(String point) {
String[] latlng = point.split(",");
if(latlng.length != 2) {
return null;
} else {
double latitude;
double longitude;
try {
latitude = Double.parseDouble(latlng[0]);
longitude = Double.parseDouble(latlng[1]);
} catch (NumberFormatException var7) {
return null;
}
return Point.newBuilder().setLatitude(latitude).setLongitude(longitude).buildPartial();
}
}
public static Polygon toPolygon(String str) {
if(str == null) {
return null;
} else {
String[] pointStrs = str.trim().split("\\s+");
if(!CapUtil.isEmptyOrWhitespace(str) && pointStrs.length != 0) {
com.google.publicalerts.cap.Polygon.Builder polygon = Polygon.newBuilder();
String[] var3 = pointStrs;
int var4 = pointStrs.length;
for(int var5 = 0; var5 < var4; ++var5) {
String pointStr = var3[var5];
Point point = toPoint(pointStr);
if(point == null) {
return null;
}
polygon.addPoint(point);
}
return polygon.buildPartial();
} else {
return null;
}
}
}
public static Polygon toPolygonNullIfTooFew(String str) {
Polygon polygon = toPolygon(str);
return polygon != null && polygon.getPointCount() < 4?null:polygon;
}
public final Alert parseFrom(String str) throws CapException, NotCapException, SAXParseException {
return this.parseFromInternal(new CachedSaxInputSource(str));
}
public final Alert parseFrom(String str, com.google.publicalerts.cap.Reasons.Builder reasons) throws NotCapException, SAXParseException {
return this.parseFromInternal(new CachedSaxInputSource(str), reasons);
}
public final Alert parseFrom(Reader reader) throws CapException, NotCapException, SAXParseException {
return this.parseFromInternal(new CachedSaxInputSource(reader));
}
public final Alert parseFrom(Reader reader, com.google.publicalerts.cap.Reasons.Builder reasons) throws NotCapException, SAXParseException {
return this.parseFromInternal(new CachedSaxInputSource(reader), reasons);
}
public final Alert parseFrom(InputSource is) throws CapException, NotCapException, SAXParseException {
return this.parseFromInternal(new CachedSaxInputSource(is));
}
public final Alert parseFrom(InputSource is, com.google.publicalerts.cap.Reasons.Builder reasons) throws NotCapException, SAXParseException {
return this.parseFromInternal(new CachedSaxInputSource(is), reasons);
}
private Alert parseFromInternal(CachedSaxInputSource is) throws CapException, NotCapException, SAXParseException {
com.google.publicalerts.cap.Reasons.Builder reasonsBuilder = Reasons.newBuilder();
Alert alert = this.parseFromInternal(is, reasonsBuilder);
Reasons reasons = reasonsBuilder.build();
if(this.validate && reasons.containsWithLevelOrHigher(Level.ERROR)) {
throw new CapException(reasons);
} else {
return alert;
}
}
protected Alert parseFromInternal(CachedSaxInputSource is, com.google.publicalerts.cap.Reasons.Builder reasons) throws NotCapException, SAXParseException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
CapXmlParser.CapXmlHandler handler = new CapXmlParser.CapXmlHandler();
try {
String alert = this.getXmlns(is);
if(!this.schemaMap.containsKey(alert)) {
throw new NotCapException("Unsupported xmlns:" + alert);
}
is.reset();
factory.setSchema((Schema)this.schemaMap.get(alert));
XMLReader reader = XmlUtil.getXMLReader(factory);
reader.setContentHandler(handler);
reader.setErrorHandler(handler);
reader.parse(is);
} catch (IOException var7) {
throw new RuntimeException(var7);
} catch (SAXException var8) {
if(var8 instanceof SAXParseException) {
throw (SAXParseException)var8;
}
throw new RuntimeException(var8);
} catch (ParserConfigurationException var9) {
throw new RuntimeException(var9);
}
reasons.addAll(handler.getReasons());
Alert alert1 = handler.getAlert();
if(this.schemaMap != STRICT_SCHEMA_MAP) {
reasons.addAll((new CapValidator()).validateAlert(alert1));
}
return alert1;
}
private String getXmlns(CachedSaxInputSource is) throws ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
XMLReader reader = XmlUtil.getXMLReader(factory);
reader.setContentHandler(new CapXmlParser.XmlnsHandler());
try {
reader.parse(is);
return null;
} catch (CapXmlParser.AbortXmlnsParseException var5) {
return var5.xmlns;
}
}
static class CapXmlHandler extends DefaultHandler {
private final StringBuilder characters = new StringBuilder();
private final Stack<com.google.protobuf.Message.Builder> builderStack = new Stack();
private final Stack<String> builderNameStack = new Stack();
private final com.google.publicalerts.cap.Reasons.Builder reasons = Reasons.newBuilder();
private final XPath xPath = new XPath();
private boolean inSignature;
private com.google.publicalerts.cap.Alert.Builder alertBuilder;
private Alert alert;
private String localName;
public CapXmlHandler() {
}
public Alert getAlert() {
return this.alert;
}
public Reasons getReasons() {
return this.reasons.build();
}
public void error(SAXParseException e) throws SAXException {
this.reasons.add(new Reason(this.xPath.toString(), ReasonType.OTHER, new Object[]{e.getMessage(), this.localName, this.characters.toString()}));
}
public void fatalError(SAXParseException e) throws SAXException {
this.error(e);
}
public void warning(SAXParseException e) throws SAXException {
this.error(e);
}
public void startElement(String uri, String localName, String qName, Attributes attributes) {
this.localName = localName;
this.characters.setLength(0);
if(!this.inSignature) {
FieldDescriptor fd = this.getField(localName);
if(this.builderStack.isEmpty()) {
if(!"alert".equals(localName) || !CapValidator.CAP_XML_NAMESPACES.contains(uri)) {
throw new NotCapException();
}
this.alertBuilder = Alert.newBuilder();
this.alertBuilder.setXmlns(uri);
this.builderStack.push(this.alertBuilder);
this.builderNameStack.push(localName);
this.xPath.push("alert");
} else if("Signature".equals(localName)) {
this.inSignature = true;
} else if(fd != null) {
if(fd.getType() == Type.MESSAGE) {
this.builderStack.push(((com.google.protobuf.Message.Builder)this.builderStack.peek()).newBuilderForField(fd));
this.builderNameStack.push(localName);
}
this.xPath.push(localName);
}
}
}
public void endElement(String uri, String localName, String qName) {
this.localName = localName;
if(this.inSignature) {
if("Signature".equals(localName)) {
this.inSignature = false;
}
} else {
FieldDescriptor fd = this.getField(localName);
if(fd == null) {
if(localName.equals(this.builderNameStack.peek())) {
com.google.protobuf.Message.Builder finishedBuilder;
if(this.builderStack.size() == 1) {
this.builderNameStack.pop();
finishedBuilder = (com.google.protobuf.Message.Builder)this.builderStack.pop();
if(finishedBuilder != null) {
this.alert = (Alert)finishedBuilder.buildPartial();
}
} else {
this.builderNameStack.pop();
finishedBuilder = (com.google.protobuf.Message.Builder)this.builderStack.pop();
fd = this.getField(localName);
if(fd != null) {
this.setOrAdd(fd, this.getComplexValue(finishedBuilder, this.characters.toString()));
this.characters.setLength(0);
this.xPath.pop();
}
}
}
} else {
this.setOrAdd(fd, this.getPrimitiveValue(fd, this.characters.toString()));
this.characters.setLength(0);
if(fd != null) {
this.xPath.pop();
}
}
}
}
public void characters(char[] ch, int start, int length) {
this.characters.append(ch, start, length);
}
void setOrAdd(FieldDescriptor fd, Object value) {
this.setOrAdd((com.google.protobuf.Message.Builder)this.builderStack.peek(), fd, value);
}
void setOrAdd(com.google.protobuf.Message.Builder builder, FieldDescriptor fd, Object value) {
if(value != null) {
if(fd.isRepeated()) {
builder.addRepeatedField(fd, value);
} else if(!builder.hasField(fd)) {
builder.setField(fd, value);
}
}
}
private FieldDescriptor getField(String localName) {
return this.builderStack.isEmpty()?null:CapUtil.findFieldByName((com.google.protobuf.Message.Builder)this.builderStack.peek(), localName);
}
Object getPrimitiveValue(FieldDescriptor fd, String val) {
switch(null.$SwitchMap$com$google$protobuf$Descriptors$FieldDescriptor$Type[fd.getType().ordinal()]) {
case 1:
case 2:
case 3:
try {
return Integer.valueOf(Integer.parseInt(val));
} catch (NumberFormatException var11) {
;
} catch (NullPointerException var12) {
;
}
break;
case 4:
case 5:
case 6:
case 7:
case 8:
try {
return Long.valueOf(Long.parseLong(val));
} catch (NumberFormatException var9) {
;
} catch (NullPointerException var10) {
;
}
break;
case 9:
try {
return Double.valueOf(Double.parseDouble(val));
} catch (NumberFormatException var7) {
;
} catch (NullPointerException var8) {
;
}
break;
case 10:
return Boolean.valueOf(Boolean.parseBoolean(val));
case 11:
return val;
case 12:
try {
return Float.valueOf(Float.parseFloat(val));
} catch (NumberFormatException var5) {
;
} catch (NullPointerException var6) {
;
}
break;
case 13:
return ByteString.copyFromUtf8(val);
case 14:
if("Very Likely".equals(val)) {
val = "VERY_LIKELY";
} else if(val != null) {
val = CapUtil.underscoreCase(val).toUpperCase();
}
EnumDescriptor enumType = fd.getEnumType();
EnumValueDescriptor evd = enumType.findValueByName(val);
if(evd == null) {
evd = enumType.findValueByName(val + "_" + enumType.getName().toUpperCase());
}
if(evd == null) {
return null;
}
return evd;
case 15:
case 16:
case 17:
case 18:
default:
throw new IllegalArgumentException(fd.getName() + " has unsupported type " + fd.getType());
}
return null;
}
MessageOrBuilder getComplexValue(com.google.protobuf.Message.Builder builder, String str) {
Object message;
if(builder instanceof com.google.publicalerts.cap.Polygon.Builder) {
message = this.toPolygonWithErrors(str);
} else if(builder instanceof com.google.publicalerts.cap.Circle.Builder) {
message = CapXmlParser.toCircle(str);
} else if(builder instanceof com.google.publicalerts.cap.Group.Builder) {
message = this.toGroup(str);
} else if(builder instanceof com.google.publicalerts.cap.ValuePair.Builder && "http://www.incident.com/cap/1.0".equals(this.alertBuilder.getXmlns())) {
message = this.toCap10ValuePair(str);
} else {
message = builder.buildPartial();
}
return (MessageOrBuilder)message;
}
Polygon toPolygonWithErrors(String str) {
Polygon polygon = CapXmlParser.toPolygon(str);
if(polygon == null) {
this.reasons.add(new Reason(this.xPath.toString(), ReasonType.INVALID_POLYGON, new Object[]{str.length() > 50?str.substring(0, 47) + "...":str}));
return null;
} else {
if(polygon.getPointCount() < 4) {
this.reasons.add(new Reason(this.xPath.toString(), ReasonType.INVALID_POLYGON, new Object[]{str.length() > 50?str.substring(0, 47) + "...":str}));
}
return polygon;
}
}
Group toGroup(String str) {
com.google.publicalerts.cap.Group.Builder group = Group.newBuilder();
boolean inQuotes = false;
boolean lastWasEscape = false;
char[] chars = str.toCharArray();
StringBuilder sb = new StringBuilder();
char[] var7 = chars;
int var8 = chars.length;
for(int var9 = 0; var9 < var8; ++var9) {
char ch = var7[var9];
boolean unescapedQuote = false;
if(ch == 34 && !lastWasEscape) {
inQuotes = !inQuotes;
unescapedQuote = true;
}
if(!inQuotes && Character.isWhitespace(ch)) {
if(sb.length() > 0) {
group.addValue(sb.toString());
sb.setLength(0);
}
} else if(!unescapedQuote) {
sb.append(ch);
}
lastWasEscape = ch == 47;
}
if(sb.length() > 0) {
group.addValue(sb.toString());
}
if(group.getValueCount() == 0) {
return null;
} else {
return group.buildPartial();
}
}
ValuePair toCap10ValuePair(String str) {
String[] nameValue = str.split("=");
return nameValue.length != 2?null:ValuePair.newBuilder().setValueName(nameValue[0].trim()).setValue(nameValue[1].trim()).build();
}
}
static class XmlnsHandler extends DefaultHandler {
XmlnsHandler() {
}
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if("alert".equals(localName) && CapValidator.CAP_XML_NAMESPACES.contains(uri)) {
throw new CapXmlParser.AbortXmlnsParseException(uri);
} else {
throw new NotCapException();
}
}
}
static class AbortXmlnsParseException extends RuntimeException {
private static final long serialVersionUID = 6359526632284475695L;
private final String xmlns;
public AbortXmlnsParseException(String xmlns) {
super(xmlns);
this.xmlns = xmlns;
}
}
}