• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 android.processor.compat.changeid;
18 
19 import org.w3c.dom.Document;
20 import org.w3c.dom.Element;
21 
22 import java.io.IOException;
23 import java.io.OutputStream;
24 
25 import javax.xml.parsers.DocumentBuilder;
26 import javax.xml.parsers.DocumentBuilderFactory;
27 import javax.xml.parsers.ParserConfigurationException;
28 import javax.xml.transform.Transformer;
29 import javax.xml.transform.TransformerException;
30 import javax.xml.transform.TransformerFactory;
31 import javax.xml.transform.dom.DOMSource;
32 import javax.xml.transform.stream.StreamResult;
33 
34 /**
35  * <p>Writes an XML config file containing provided changes.</p>
36  * <p>Output example:</p>
37  * <pre>
38  * {@code
39  * <config>
40  *     <compat-change id="111" name="change-name1">
41  *         <meta-data definedIn="java.package.ClassName" sourcePosition="java/package/ClassName
42  *         .java:10" />
43  *     </compat-change>
44  *     <compat-change disabled="true" id="222" loggingOnly= "true" name="change-name2"
45  *     description="my change">
46  *         <meta-data .../>
47  *     </compat-change>
48  *     <compat-change enableAfterTargetSdk="28" id="333" name="change-name3">
49  *         <meta-data .../>
50  *     </compat-change>
51  * </config>
52  *  }
53  *
54  * </pre>
55  *
56  * The inner {@code meta-data} tags are intended to be stripped before embedding the config on a
57  * device. They are intended for use by intermediate build tools only.
58  */
59 final class XmlWriter {
60     //XML tags
61     private static final String XML_ROOT = "config";
62     private static final String XML_CHANGE_ELEMENT = "compat-change";
63     private static final String XML_NAME_ATTR = "name";
64     private static final String XML_ID_ATTR = "id";
65     private static final String XML_DISABLED_ATTR = "disabled";
66     private static final String XML_LOGGING_ATTR = "loggingOnly";
67     private static final String XML_ENABLED_AFTER_ATTR = "enableAfterTargetSdk";
68     private static final String XML_ENABLED_SINCE_ATTR = "enableSinceTargetSdk";
69     private static final String XML_DESCRIPTION_ATTR = "description";
70     private static final String XML_OVERRIDABLE_ATTR = "overridable";
71     private static final String XML_METADATA_ELEMENT = "meta-data";
72     private static final String XML_DEFINED_IN = "definedIn";
73     private static final String XML_SOURCE_POSITION = "sourcePosition";
74 
75     private Document mDocument;
76     private Element mRoot;
77 
XmlWriter()78     XmlWriter() {
79         mDocument = createDocument();
80         mRoot = mDocument.createElement(XML_ROOT);
81         mDocument.appendChild(mRoot);
82     }
83 
addChange(Change change)84     void addChange(Change change) {
85         Element newElement = mDocument.createElement(XML_CHANGE_ELEMENT);
86         newElement.setAttribute(XML_NAME_ATTR, change.name);
87         newElement.setAttribute(XML_ID_ATTR, change.id.toString());
88         if (change.disabled) {
89             newElement.setAttribute(XML_DISABLED_ATTR, "true");
90         }
91         if (change.loggingOnly) {
92             newElement.setAttribute(XML_LOGGING_ATTR, "true");
93         }
94         if (change.enabledAfter != null) {
95             newElement.setAttribute(XML_ENABLED_AFTER_ATTR, change.enabledAfter.toString());
96         }
97         if (change.enabledSince != null) {
98             newElement.setAttribute(XML_ENABLED_SINCE_ATTR, change.enabledSince.toString());
99         }
100         if (change.description != null) {
101             newElement.setAttribute(XML_DESCRIPTION_ATTR, change.description);
102         }
103         if (change.overridable) {
104             newElement.setAttribute(XML_OVERRIDABLE_ATTR, "true");
105         }
106         Element metaData = mDocument.createElement(XML_METADATA_ELEMENT);
107         if (change.qualifiedClass != null) {
108             metaData.setAttribute(XML_DEFINED_IN, change.qualifiedClass);
109         }
110         if (change.sourcePosition != null) {
111             metaData.setAttribute(XML_SOURCE_POSITION, change.sourcePosition);
112         }
113         if (metaData.hasAttributes()) {
114             newElement.appendChild(metaData);
115         }
116         mRoot.appendChild(newElement);
117     }
118 
write(OutputStream output)119     void write(OutputStream output) throws IOException {
120         try {
121             TransformerFactory transformerFactory = TransformerFactory.newInstance();
122             Transformer transformer = transformerFactory.newTransformer();
123             DOMSource domSource = new DOMSource(mDocument);
124 
125             StreamResult result = new StreamResult(output);
126 
127             transformer.transform(domSource, result);
128         } catch (TransformerException e) {
129             throw new IOException("Failed to write output", e);
130         }
131     }
132 
createDocument()133     private Document createDocument() {
134         try {
135             DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
136             DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
137             return documentBuilder.newDocument();
138         } catch (ParserConfigurationException e) {
139             throw new RuntimeException("Failed to create a new document", e);
140         }
141     }
142 }
143