/* * Copyright (C) 2015 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.preload; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import java.io.File; import java.io.FileReader; import java.text.DateFormat; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; /** * Helper class for serialization and deserialization of a collection of DumpData objects to XML. */ public class DumpDataIO { /** * Serialize the given collection to an XML document. Returns the produced string. */ public static String serialize(Collection<DumpData> data) { // We'll do this by hand, constructing a DOM or similar is too complicated for our simple // use case. StringBuilder sb = new StringBuilder(); sb.append("<preloaded-classes-data>\n"); for (DumpData d : data) { serialize(d, sb); } sb.append("</preloaded-classes-data>\n"); return sb.toString(); } private static void serialize(DumpData d, StringBuilder sb) { sb.append("<data package=\"" + d.packageName + "\" date=\"" + DateFormat.getDateTimeInstance().format(d.date) +"\">\n"); for (Map.Entry<String, String> e : d.dumpData.entrySet()) { sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n"); } sb.append("</data>\n"); } /** * Load a collection of DumpData objects from the given file. */ public static Collection<DumpData> deserialize(File f) throws Exception { // Use SAX parsing. Our format is very simple. Don't do any schema validation or such. SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(false); SAXParser saxParser = spf.newSAXParser(); XMLReader xmlReader = saxParser.getXMLReader(); DumpDataContentHandler ddch = new DumpDataContentHandler(); xmlReader.setContentHandler(ddch); xmlReader.parse(new InputSource(new FileReader(f))); return ddch.data; } private static class DumpDataContentHandler extends DefaultHandler { Collection<DumpData> data = new LinkedList<DumpData>(); DumpData openData = null; @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("data")) { if (openData != null) { throw new IllegalStateException(); } String pkg = attributes.getValue("package"); String dateString = attributes.getValue("date"); if (pkg == null || dateString == null) { throw new IllegalArgumentException(); } try { Date date = DateFormat.getDateTimeInstance().parse(dateString); openData = new DumpData(pkg, new HashMap<String, String>(), date); } catch (Exception e) { throw new RuntimeException(e); } } else if (qName.equals("class")) { if (openData == null) { throw new IllegalStateException(); } String className = attributes.getValue("name"); String classLoader = attributes.getValue("classloader"); if (className == null || classLoader == null) { throw new IllegalArgumentException(); } openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equals("data")) { if (openData == null) { throw new IllegalStateException(); } openData.countBootClassPath(); data.add(openData); openData = null; } } } }