--- /dev/null
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed 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 com.android.ddmuilib.location;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+/**
+ * A very basic KML parser to meet the need of the emulator control panel.
+ * <p/>
+ * It parses basic Placemark information.
+ */
+public class KmlParser {
+
+ private final static String NS_KML_2 = "http://earth.google.com/kml/2."; //$NON-NLS-1$
+
+ private final static String NODE_PLACEMARK = "Placemark"; //$NON-NLS-1$
+ private final static String NODE_NAME = "name"; //$NON-NLS-1$
+ private final static String NODE_COORDINATES = "coordinates"; //$NON-NLS-1$
+
+ private final static Pattern sLocationPattern = Pattern.compile("([^,]+),([^,]+)(?:,([^,]+))?");
+
+ private static SAXParserFactory sParserFactory;
+
+ static {
+ sParserFactory = SAXParserFactory.newInstance();
+ sParserFactory.setNamespaceAware(true);
+ }
+
+ private String mFileName;
+
+ private KmlHandler mHandler;
+
+ /**
+ * Handler for the SAX parser.
+ */
+ private static class KmlHandler extends DefaultHandler {
+ // --------- parsed data ---------
+ List<WayPoint> mWayPoints;
+
+ // --------- state for parsing ---------
+ WayPoint mCurrentWayPoint;
+ final StringBuilder mStringAccumulator = new StringBuilder();
+
+ boolean mSuccess = true;
+
+ @Override
+ public void startElement(String uri, String localName, String name, Attributes attributes)
+ throws SAXException {
+ // we only care about the standard GPX nodes.
+ try {
+ if (uri.startsWith(NS_KML_2)) {
+ if (NODE_PLACEMARK.equals(localName)) {
+ if (mWayPoints == null) {
+ mWayPoints = new ArrayList<WayPoint>();
+ }
+
+ mWayPoints.add(mCurrentWayPoint = new WayPoint());
+ }
+ }
+ } finally {
+ // no matter the node, we empty the StringBuilder accumulator when we start
+ // a new node.
+ mStringAccumulator.setLength(0);
+ }
+ }
+
+ /**
+ * Processes new characters for the node content. The characters are simply stored,
+ * and will be processed when {@link #endElement(String, String, String)} is called.
+ */
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ mStringAccumulator.append(ch, start, length);
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String name) throws SAXException {
+ if (uri.startsWith(NS_KML_2)) {
+ if (NODE_PLACEMARK.equals(localName)) {
+ mCurrentWayPoint = null;
+ } else if (NODE_NAME.equals(localName)) {
+ if (mCurrentWayPoint != null) {
+ mCurrentWayPoint.setName(mStringAccumulator.toString());
+ }
+ } else if (NODE_COORDINATES.equals(localName)) {
+ if (mCurrentWayPoint != null) {
+ parseLocation(mCurrentWayPoint, mStringAccumulator.toString());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void error(SAXParseException e) throws SAXException {
+ mSuccess = false;
+ }
+
+ @Override
+ public void fatalError(SAXParseException e) throws SAXException {
+ mSuccess = false;
+ }
+
+ /**
+ * Parses the location string and store the information into a {@link LocationPoint}.
+ * @param locationNode the {@link LocationPoint} to receive the location data.
+ * @param location The string containing the location info.
+ */
+ private void parseLocation(LocationPoint locationNode, String location) {
+ Matcher m = sLocationPattern.matcher(location);
+ if (m.matches()) {
+ try {
+ double longitude = Double.parseDouble(m.group(1));
+ double latitude = Double.parseDouble(m.group(2));
+
+ locationNode.setLocation(longitude, latitude);
+
+ if (m.groupCount() == 3) {
+ // looks like we have elevation data.
+ locationNode.setElevation(Double.parseDouble(m.group(3)));
+ }
+ } catch (NumberFormatException e) {
+ // wrong data, do nothing.
+ }
+ }
+ }
+
+ WayPoint[] getWayPoints() {
+ if (mWayPoints != null) {
+ return mWayPoints.toArray(new WayPoint[mWayPoints.size()]);
+ }
+
+ return null;
+ }
+
+ boolean getSuccess() {
+ return mSuccess;
+ }
+ }
+
+ /**
+ * Creates a new GPX parser for a file specified by its full path.
+ * @param fileName The full path of the GPX file to parse.
+ */
+ public KmlParser(String fileName) {
+ mFileName = fileName;
+ }
+
+ /**
+ * Parses the GPX file.
+ * @return <code>true</code> if success.
+ */
+ public boolean parse() {
+ try {
+ SAXParser parser = sParserFactory.newSAXParser();
+
+ mHandler = new KmlHandler();
+
+ parser.parse(new InputSource(new FileReader(mFileName)), mHandler);
+
+ return mHandler.getSuccess();
+ } catch (ParserConfigurationException e) {
+ } catch (SAXException e) {
+ } catch (IOException e) {
+ } finally {
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the parsed {@link WayPoint} objects, or <code>null</code> if none were found (or
+ * if the parsing failed.
+ */
+ public WayPoint[] getWayPoints() {
+ if (mHandler != null) {
+ return mHandler.getWayPoints();
+ }
+
+ return null;
+ }
+}