blob: ad00cf4713c1309969c1dc6d798c1438e7a39b52 [file] [log] [blame]
/*
* Copyright (C) 2012 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.apigenerator;
import com.android.utils.Pair;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
/**
* Represents a class and its methods/fields.
* This is used to write the simplified XML file containing all the public API.
*
*/
@SuppressWarnings({"UnnecessaryUnboxing", "UnnecessaryBoxing"})
public class ApiClass {
private final String mName;
private final int mSince;
private final List<Pair<String, Integer>> mSuperClasses =
new ArrayList<Pair<String, Integer>>();
private final List<Pair<String, Integer>> mInterfaces = new ArrayList<Pair<String, Integer>>();
private final Map<String, Integer> mFields = new HashMap<String, Integer>();
private final Map<String, Integer> mMethods = new HashMap<String, Integer>();
private final Map<String, Integer> mDeprecated = new HashMap<String, Integer>();
public ApiClass(String name, int since) {
mName = name;
mSince = since;
}
public int deprecatedIn = Integer.MAX_VALUE;
public String getName() {
return mName;
}
int getSince() {
return mSince;
}
public void addField(String name, int since, boolean deprecated) {
Integer i = mFields.get(name);
if (i == null || i.intValue() > since) {
mFields.put(name, Integer.valueOf(since));
}
if (deprecated) {
i = mDeprecated.get(name);
if (i == null || i.intValue() > since) {
mDeprecated.put(name, Integer.valueOf(since));
}
}
}
public void addMethod(String name, int since, boolean deprecated) {
Integer i = mMethods.get(name);
if (i == null || i.intValue() > since) {
mMethods.put(name, Integer.valueOf(since));
}
if (deprecated) {
i = mDeprecated.get(name);
if (i == null || i.intValue() > since) {
mDeprecated.put(name, Integer.valueOf(since));
}
}
}
public Map<String, Integer> getMethods() {
return mMethods;
}
public void replaceMethods(Map<String, Integer> fixedMethods) {
mMethods.clear();
mMethods.putAll(fixedMethods);
}
public void addSuperClass(String superClass, int since) {
addToArray(mSuperClasses, superClass, since);
}
public List<Pair<String, Integer>> getSuperClasses() {
return mSuperClasses;
}
public void addInterface(String interfaceClass, int since) {
addToArray(mInterfaces, interfaceClass, since);
}
public List<Pair<String, Integer>> getInterfaces() {
return mInterfaces;
}
void addToArray(List<Pair<String, Integer>> list, String name, int value) {
// check if we already have that name (at a lower level)
for (Pair<String, Integer> pair : list) {
if (name.equals(pair.getFirst()) && pair.getSecond() < value) {
return;
}
}
list.add(Pair.of(name, Integer.valueOf(value)));
}
public void print(PrintStream stream) {
stream.print("\t<class name=\"");
stream.print(mName);
stream.print("\" since=\"");
stream.print(mSince);
if (deprecatedIn < Integer.MAX_VALUE) {
stream.print("\" deprecated=\"");
stream.print(deprecatedIn);
}
stream.println("\">");
print(mSuperClasses, "extends", stream);
print(mInterfaces, "implements", stream);
print(mMethods, "method", stream);
print(mFields, "field", stream);
stream.println("\t</class>");
}
private void print(List<Pair<String, Integer> > list, String name, PrintStream stream) {
Collections.sort(list, new Comparator<Pair<String, Integer> >() {
@Override
public int compare(Pair<String, Integer> o1, Pair<String, Integer> o2) {
return o1.getFirst().compareTo(o2.getFirst());
}
});
for (Pair<String, Integer> pair : list) {
Integer deprecated = mDeprecated.get(pair.getFirst());
if (mSince == pair.getSecond()) {
stream.print("\t\t<");
stream.print(name);
stream.print(" name=\"");
stream.print(encodeAttribute(pair.getFirst()));
if (deprecated != null) {
stream.print("\" deprecated=\"");
stream.print(deprecated);
}
stream.println("\" />");
} else {
stream.print("\t\t<");
stream.print(name);
stream.print(" name=\"");
stream.print(encodeAttribute(pair.getFirst()));
stream.print("\" since=\"");
stream.print(pair.getSecond());
if (deprecated != null) {
stream.print("\" deprecated=\"");
stream.print(deprecated);
}
stream.println("\" />");
}
}
}
private void print(Map<String, Integer> map, String name, PrintStream stream) {
TreeMap<String, Integer> map2 = new TreeMap<String, Integer>(map);
for (Entry<String, Integer> entry : map2.entrySet()) {
Integer deprecated = mDeprecated.get(entry.getKey());
if (mSince == entry.getValue()) {
stream.print("\t\t<");
stream.print(name);
stream.print(" name=\"");
stream.print(encodeAttribute(entry.getKey()));
if (deprecated != null) {
stream.print("\" deprecated=\"");
stream.print(deprecated);
}
stream.println("\" />");
} else {
stream.print("\t\t<");
stream.print(name);
stream.print(" name=\"");
stream.print(encodeAttribute(entry.getKey()));
stream.print("\" since=\"");
stream.print(entry.getValue());
if (deprecated != null) {
stream.print("\" deprecated=\"");
stream.print(deprecated);
}
stream.println("\" />");
}
}
}
private String encodeAttribute(String attribute) {
StringBuilder sb = new StringBuilder();
int n = attribute.length();
// &, ", ' and < are illegal in attributes; see http://www.w3.org/TR/REC-xml/#NT-AttValue
// (' legal in a " string and " is legal in a ' string but here we'll stay on the safe
// side)
for (int i = 0; i < n; i++) {
char c = attribute.charAt(i);
if (c == '"') {
sb.append("&quot;"); //$NON-NLS-1$
} else if (c == '<') {
sb.append("&lt;"); //$NON-NLS-1$
} else if (c == '\'') {
sb.append("&apos;"); //$NON-NLS-1$
} else if (c == '&') {
sb.append("&amp;"); //$NON-NLS-1$
} else {
sb.append(c);
}
}
return sb.toString();
}
@Override
public String toString() {
return mName;
}
}