/*
* Copyright (C) 2012 Sony Mobile Communications AB
*
* This file is part of ApkAnalyser.
*
* 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 jerl.bcm.util;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import jerl.bcm.inj.InjectionClass;
import jerl.bcm.inj.InjectionMethod;
import jerl.blockformat.BFParseException;
import jerl.blockformat.BFReader;
import jerl.blockformat.BFVisitor;
public class InjectionBuilder implements BFVisitor {
private String curClassName = null;
private String curInjClassName = null;
private String curArgTypes = null;
private String[] curArgValues = null;
private final BFReader pr;
// key = class name, value = list of injections
private final Hashtable<String, ClassInjContainer> injectionTable = new Hashtable<String, ClassInjContainer>();
public InjectionBuilder(InputStream is) throws IOException, BFParseException {
pr = new BFReader(is);
pr.accept(this);
}
/**
* Returns enumeration of all class names that have method injections.
* @return
*/
public Enumeration<String> getClassNamesForInjections() {
return injectionTable.keys();
}
public ClassInjContainer getClassInjContainer(String className) {
return injectionTable.get(className);
}
@Override
public void visitBeginBlock(String blockName, String[] args) {
if (blockName.equals("class")) {
// start of class block
String className = args[0];
curClassName = className;
} else if (blockName.equals("injection")) {
int n = Integer.parseInt(args[0]);
curInjClassName = null;
curArgTypes = null;
curArgValues = new String[n];
}
}
@Override
public void visitEndBlock(String blockName) {
if (blockName.equals("class")) {
// end of class block
curClassName = null;
} else if (blockName.equals("injection")) {
// create an injection instance and add to injectionTable
Object obj = null;
try {
obj = createInjection(curInjClassName, curArgTypes, curArgValues);
} catch (Exception e) {
System.err.println("Class: " + curInjClassName + ", argtypes=" + curArgTypes + ", argValues=" + curArgValues);
e.printStackTrace();
}
if (obj != null) {
if (obj instanceof InjectionMethod) {
addMethodInjection(curClassName, (InjectionMethod) obj);
} else if (obj instanceof InjectionClass) {
addClassInjection(curClassName, (InjectionClass) obj);
} else {
System.err.println("WARNING: Unknown class type");
}
}
}
}
@Override
public void visitProperty(String key, String value) {
if (key.equals("ClassName")) {
curInjClassName = value;
} else if (key.equals("ArgTypes")) {
curArgTypes = value;
} else if (key.startsWith("ArgValue")) {
int i = parseIndex(key);
curArgValues[i] = value;
} else {
System.out.println("Unknown property: key='" + key + "', value='" + value + "'");
}
}
@Override
public String toString() {
return injectionTable.toString();
}
private void addMethodInjection(String className, InjectionMethod inj) {
ClassInjContainer c = injectionTable.get(className);
if (c == null) {
c = new ClassInjContainer(className);
injectionTable.put(className, c);
}
c.addMethodInjection(inj);
}
private void addClassInjection(String className, InjectionClass inj) {
ClassInjContainer c = injectionTable.get(className);
if (c == null) {
c = new ClassInjContainer(className);
injectionTable.put(className, c);
}
c.addClassInjection(inj);
}
private static int parseIndex(String str) {
int beginIndex = str.indexOf('[');
int endIndex = str.indexOf(']', beginIndex);
return Integer.parseInt(str.substring(beginIndex + 1, endIndex).trim());
}
private static Object[] createParameters(String types, String[] values) {
StringTokenizer st = new StringTokenizer(types, ",");
String[] aTypes = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
aTypes[i] = st.nextToken().trim();
}
Object[] ret = new Object[aTypes.length];
// loop for all types, create object from value string
for (int i = 0; i < aTypes.length; i++) {
if (aTypes[i].equals(InjectionUtil.STRING_TYPE)) {
ret[i] = values[i];
} else if (aTypes[i].equals(InjectionUtil.INT_TYPE)) {
ret[i] = Integer.valueOf(values[i]);
} else if (aTypes[i].equals(InjectionUtil.BOOLEAN_TYPE)) {
ret[i] = Boolean.valueOf(values[i]);
} else {
System.err.println("ERROR: unknown type, '" + aTypes[i] + "'");
}
}
return ret;
}
private static Object createInjection(String className, String types, String[] values) throws Exception {
Class<?> cls = Class.forName(className);
Constructor<?>[] a = cls.getConstructors();
int i = 0;
for (i = 0; i < a.length; i++) {
String tmp = a[i].toString();
if (tmp.indexOf("(" + types + ")") != -1) {
break;
}
}
if (i == a.length) {
// unable to find constructor
System.err.println("ERROR: unable to find constructor");
return null;
}
try {
Object[] initargs = createParameters(types, values);
Object obj = a[i].newInstance(initargs);
return obj;
} catch (Exception e) {
//e.printStackTrace();
throw new Exception("ClassName='" + className + "', types='" + types + "'");
}
}
}