• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.tools.metalava.apilevels;
17 
18 import org.jetbrains.annotations.NotNull;
19 
20 import java.io.PrintStream;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.List;
25 
26 /**
27  * Represents an API element, e.g. class, method or field.
28  */
29 public class ApiElement implements Comparable<ApiElement> {
30     private final String mName;
31     private int mSince;
32     private int mDeprecatedIn;
33     private int mLastPresentIn;
34 
35     /**
36      * @param name       the name of the API element
37      * @param version    an API version for which the API element existed
38      * @param deprecated whether the API element was deprecated in the API version in question
39      */
ApiElement(String name, int version, boolean deprecated)40     ApiElement(String name, int version, boolean deprecated) {
41         assert name != null;
42         assert version > 0;
43         mName = name;
44         mSince = version;
45         mLastPresentIn = version;
46         if (deprecated) {
47             mDeprecatedIn = version;
48         }
49     }
50 
51     /**
52      * @param name    the name of the API element
53      * @param version an API version for which the API element existed
54      */
ApiElement(String name, int version)55     ApiElement(String name, int version) {
56         this(name, version, false);
57     }
58 
ApiElement(String name)59     ApiElement(String name) {
60         assert name != null;
61         mName = name;
62     }
63 
64     /**
65      * Returns the name of the API element.
66      */
getName()67     public final String getName() {
68         return mName;
69     }
70 
getSince()71     public int getSince() {
72         return mSince;
73     }
74 
75     /**
76      * Checks if this API element was introduced not later than another API element.
77      *
78      * @param other the API element to compare to
79      * @return true if this API element was introduced not later than {@code other}
80      */
introducedNotLaterThan(ApiElement other)81     final boolean introducedNotLaterThan(ApiElement other) {
82         return mSince <= other.mSince;
83     }
84 
85     /**
86      * Updates the API element with information for a specific API version.
87      *
88      * @param version    an API version for which the API element existed
89      * @param deprecated whether the API element was deprecated in the API version in question
90      */
update(int version, boolean deprecated)91     void update(int version, boolean deprecated) {
92         assert version > 0;
93         if (mSince > version) {
94             mSince = version;
95         }
96         if (mLastPresentIn < version) {
97             mLastPresentIn = version;
98         }
99         if (deprecated) {
100             if (mDeprecatedIn == 0 || mDeprecatedIn > version) {
101                 mDeprecatedIn = version;
102             }
103         }
104     }
105 
106     /**
107      * Updates the API element with information for a specific API version.
108      *
109      * @param version an API version for which the API element existed
110      */
update(int version)111     public void update(int version) {
112         update(version, isDeprecated());
113     }
114 
115     /**
116      * Checks whether the API element is deprecated or not.
117      */
isDeprecated()118     public final boolean isDeprecated() {
119         return mDeprecatedIn != 0;
120     }
121 
122     /**
123      * Prints an XML representation of the element to a stream terminated by a line break.
124      * Attributes with values matching the parent API element are omitted.
125      *
126      * @param tag           the tag of the XML element
127      * @param parentElement the parent API element
128      * @param indent        the whitespace prefix to insert before the XML element
129      * @param stream        the stream to print the XML element to
130      */
print(String tag, ApiElement parentElement, String indent, PrintStream stream)131     void print(String tag, ApiElement parentElement, String indent, PrintStream stream) {
132         print(tag, true, parentElement, indent, stream);
133     }
134 
135     /**
136      * Prints an XML representation of the element to a stream terminated by a line break.
137      * Attributes with values matching the parent API element are omitted.
138      *
139      * @param tag           the tag of the XML element
140      * @param closeTag      if true the XML element is terminated by "/>", otherwise the closing
141      *                      tag of the element is not printed
142      * @param parentElement the parent API element
143      * @param indent        the whitespace prefix to insert before the XML element
144      * @param stream        the stream to print the XML element to
145      * @see #printClosingTag(String, String, PrintStream)
146      */
print(String tag, boolean closeTag, ApiElement parentElement, String indent, PrintStream stream)147     void print(String tag, boolean closeTag, ApiElement parentElement, String indent,
148                PrintStream stream) {
149         stream.print(indent);
150         stream.print('<');
151         stream.print(tag);
152         stream.print(" name=\"");
153         stream.print(encodeAttribute(mName));
154         if (mSince > parentElement.mSince) {
155             stream.print("\" since=\"");
156             stream.print(mSince);
157         }
158         if (mDeprecatedIn != 0) {
159             stream.print("\" deprecated=\"");
160             stream.print(mDeprecatedIn);
161         }
162         if (mLastPresentIn < parentElement.mLastPresentIn) {
163             stream.print("\" removed=\"");
164             stream.print(mLastPresentIn + 1);
165         }
166         stream.print('"');
167         if (closeTag) {
168             stream.print('/');
169         }
170         stream.println('>');
171     }
172 
173     /**
174      * Prints homogeneous XML elements to a stream. Each element is printed on a separate line.
175      * Attributes with values matching the parent API element are omitted.
176      *
177      * @param elements the elements to print
178      * @param tag      the tag of the XML elements
179      * @param indent   the whitespace prefix to insert before each XML element
180      * @param stream   the stream to print the XML elements to
181      */
print(Collection<? extends ApiElement> elements, String tag, String indent, PrintStream stream)182     void print(Collection<? extends ApiElement> elements, String tag, String indent, PrintStream stream) {
183         for (ApiElement element : sortedList(elements)) {
184             element.print(tag, this, indent, stream);
185         }
186     }
187 
sortedList(Collection<T> elements)188     private <T extends ApiElement> List<T> sortedList(Collection<T> elements) {
189         List<T> list = new ArrayList<>(elements);
190         Collections.sort(list);
191         return list;
192     }
193 
194     /**
195      * Prints a closing tag of an XML element terminated by a line break.
196      *
197      * @param tag    the tag of the element
198      * @param indent the whitespace prefix to insert before the closing tag
199      * @param stream the stream to print the XML element to
200      */
printClosingTag(String tag, String indent, PrintStream stream)201     static void printClosingTag(String tag, String indent, PrintStream stream) {
202         stream.print(indent);
203         stream.print("</");
204         stream.print(tag);
205         stream.println('>');
206     }
207 
encodeAttribute(String attribute)208     private static String encodeAttribute(String attribute) {
209         StringBuilder sb = new StringBuilder();
210         int n = attribute.length();
211         // &, ", ' and < are illegal in attributes; see http://www.w3.org/TR/REC-xml/#NT-AttValue
212         // (' legal in a " string and " is legal in a ' string but here we'll stay on the safe side).
213         for (int i = 0; i < n; i++) {
214             char c = attribute.charAt(i);
215             if (c == '"') {
216                 sb.append("&quot;"); //$NON-NLS-1$
217             } else if (c == '<') {
218                 sb.append("&lt;"); //$NON-NLS-1$
219             } else if (c == '\'') {
220                 sb.append("&apos;"); //$NON-NLS-1$
221             } else if (c == '&') {
222                 sb.append("&amp;"); //$NON-NLS-1$
223             } else {
224                 sb.append(c);
225             }
226         }
227 
228         return sb.toString();
229     }
230 
231     @Override
compareTo(@otNull ApiElement other)232     public int compareTo(@NotNull ApiElement other) {
233         return mName.compareTo(other.mName);
234     }
235 }
236