/* * 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; } }