1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.preload;
18 
19 import org.xml.sax.Attributes;
20 import org.xml.sax.InputSource;
21 import org.xml.sax.SAXException;
22 import org.xml.sax.XMLReader;
23 import org.xml.sax.helpers.DefaultHandler;
24 
25 import java.io.File;
26 import java.io.FileReader;
27 import java.text.DateFormat;
28 import java.util.Collection;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.LinkedList;
32 import java.util.Map;
33 
34 import javax.xml.parsers.SAXParser;
35 import javax.xml.parsers.SAXParserFactory;
36 
37 /**
38  * Helper class for serialization and deserialization of a collection of DumpData objects to XML.
39  */
40 public class DumpDataIO {
41 
42   /**
43    * Serialize the given collection to an XML document. Returns the produced string.
44    */
serialize(Collection<DumpData> data)45   public static String serialize(Collection<DumpData> data) {
46       // We'll do this by hand, constructing a DOM or similar is too complicated for our simple
47       // use case.
48 
49       StringBuilder sb = new StringBuilder();
50       sb.append("<preloaded-classes-data>\n");
51 
52       for (DumpData d : data) {
53           serialize(d, sb);
54       }
55 
56       sb.append("</preloaded-classes-data>\n");
57       return sb.toString();
58   }
59 
serialize(DumpData d, StringBuilder sb)60   private static void serialize(DumpData d, StringBuilder sb) {
61       sb.append("<data package=\"" + d.packageName + "\" date=\"" +
62               DateFormat.getDateTimeInstance().format(d.date) +"\">\n");
63 
64       for (Map.Entry<String, String> e : d.dumpData.entrySet()) {
65           sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n");
66       }
67 
68       sb.append("</data>\n");
69   }
70 
71   /**
72    * Load a collection of DumpData objects from the given file.
73    */
deserialize(File f)74   public static Collection<DumpData> deserialize(File f) throws Exception {
75       // Use SAX parsing. Our format is very simple. Don't do any schema validation or such.
76 
77       SAXParserFactory spf = SAXParserFactory.newInstance();
78       spf.setNamespaceAware(false);
79       SAXParser saxParser = spf.newSAXParser();
80 
81       XMLReader xmlReader = saxParser.getXMLReader();
82       DumpDataContentHandler ddch = new DumpDataContentHandler();
83       xmlReader.setContentHandler(ddch);
84       xmlReader.parse(new InputSource(new FileReader(f)));
85 
86       return ddch.data;
87   }
88 
89   private static class DumpDataContentHandler extends DefaultHandler {
90       Collection<DumpData> data = new LinkedList<DumpData>();
91       DumpData openData = null;
92 
93       @Override
startElement(String uri, String localName, String qName, Attributes attributes)94       public void startElement(String uri, String localName, String qName, Attributes attributes)
95               throws SAXException {
96           if (qName.equals("data")) {
97               if (openData != null) {
98                   throw new IllegalStateException();
99               }
100               String pkg = attributes.getValue("package");
101               String dateString = attributes.getValue("date");
102 
103               if (pkg == null || dateString == null) {
104                   throw new IllegalArgumentException();
105               }
106 
107               try {
108                   Date date = DateFormat.getDateTimeInstance().parse(dateString);
109                   openData = new DumpData(pkg, new HashMap<String, String>(), date);
110               } catch (Exception e) {
111                   throw new RuntimeException(e);
112               }
113           } else if (qName.equals("class")) {
114               if (openData == null) {
115                   throw new IllegalStateException();
116               }
117               String className = attributes.getValue("name");
118               String classLoader = attributes.getValue("classloader");
119 
120               if (className == null || classLoader == null) {
121                   throw new IllegalArgumentException();
122               }
123 
124               openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader);
125           }
126       }
127 
128       @Override
endElement(String uri, String localName, String qName)129       public void endElement(String uri, String localName, String qName) throws SAXException {
130           if (qName.equals("data")) {
131               if (openData == null) {
132                   throw new IllegalStateException();
133               }
134               openData.countBootClassPath();
135 
136               data.add(openData);
137               openData = null;
138           }
139       }
140   }
141 }
142