package org.bimserver.shared.meta;
/******************************************************************************
* Copyright (C) 2009-2014 BIMserver.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.xml.bind.annotation.XmlSeeAlso;
import org.bimserver.shared.interfaces.PublicInterface;
import org.bimserver.shared.reflector.ReflectorFactory;
import org.bimserver.utils.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.reflect.generics.reflectiveObjects.TypeVariableImpl;
public class SServicesMap {
private static final Logger LOGGER = LoggerFactory.getLogger(SServicesMap.class);
// Must be LinkedHashMap because order IS important for dependencies
private final Map<String, SService> servicesByName = new LinkedHashMap<String, SService>();
private final Map<String, SService> servicesBySimpleName = new LinkedHashMap<String, SService>();
private final Map<String, SClass> types = new TreeMap<String, SClass>();
private ReflectorFactory reflectorFactory;
public void add(SService sService) {
servicesByName.put(sService.getName(), sService);
servicesBySimpleName.put(sService.getSimpleName(), sService);
}
public SService getByName(String name) {
return servicesByName.get(name);
}
public Collection<SService> list() {
return servicesByName.values();
}
public SService getBySimpleName(String name) {
return servicesBySimpleName.get(name);
}
public SClass getType(String name) {
for (SService sService : servicesByName.values()) {
SClass type = sService.getServicesMap().getSType(name);
if (type != null) {
return type;
}
}
return null;
}
public Set<String> keySetName() {
return servicesByName.keySet();
}
public Set<String> keySetSimpleName() {
return servicesBySimpleName.keySet();
}
public void addType(Class<?> type) {
if (!types.containsKey(type.getSimpleName())) {
SClass sClass = new SClass(this, type, null);
types.put(sClass.getSimpleName(), sClass);
types.put(sClass.getName(), sClass);
addRelatedTypes(type);
}
}
public void addType(SClass type) {
types.put(type.getSimpleName(), type);
types.put(type.getName(), type);
addRelatedTypes(type.getInstanceClass());
}
private void addRelatedTypes(Class<?> type) {
for (Method method : type.getMethods()) {
if (method.getName().startsWith("get") && method.getName().length() > 3 && !method.getName().equals("getSClass")) {
if (type.getAnnotation(XmlSeeAlso.class) != null) {
XmlSeeAlso xmlSeeAlso = type.getAnnotation(XmlSeeAlso.class);
for (Class<?> c : xmlSeeAlso.value()) {
addType(c);
}
}
if (type.getSuperclass() != null) {
addType(type.getSuperclass());
}
String fieldName = StringUtils.firstLowerCase(method.getName().substring(3));
try {
if (type.getMethod("set" + StringUtils.firstUpperCase(fieldName), method.getReturnType()) != null) {
addType(method.getReturnType());
}
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
if (getGenericType(method) != null) {
addType(getGenericType(method));
}
for (Class<?> pt : method.getParameterTypes()) {
addType(pt);
}
}
}
}
public Class<?> getGenericType(Method method) {
Type genericReturnType = method.getGenericReturnType();
if (method.getGenericReturnType() instanceof ParameterizedType) {
ParameterizedType parameterizedTypeImpl = (ParameterizedType)genericReturnType;
Type first = parameterizedTypeImpl.getActualTypeArguments()[0];
if (first instanceof WildcardType) {
return null;
} else if (first instanceof ParameterizedType) {
return null;
} else if (first instanceof TypeVariableImpl) {
TypeVariableImpl<?> typeVariableImpl = (TypeVariableImpl<?>)first;
GenericDeclaration genericDeclaration = typeVariableImpl.getGenericDeclaration();
if (genericDeclaration instanceof Class) {
return (Class<?>) genericDeclaration;
}
} else {
return (Class<?>) first;
}
}
return (Class<?>) method.getGenericReturnType();
}
public Set<SClass> getTypes() {
return new HashSet<SClass>(types.values());
}
// public SClass getSType(String name, Set<SService> checked) {
// checked.add(this);
// SClass sType = types.get(name);
// if (sType == null) {
// if (name.contains(".")) {
// name = name.substring(name.lastIndexOf(".") + 1);
// return getSType(name, checked);
// }
// }
// if (sType == null) {
// for (SService other : others) {
// if (!checked.contains(other)) {
// SClass otherClass = other.getSType(name, checked);
// if (otherClass != null) {
// return otherClass;
// }
// }
// }
// }
// return sType;
// }
public SClass getSType(String name) {
return types.get(name);
// return getSType(name, new HashSet<SService>());
}
public Set<Class<? extends PublicInterface>> getInterfaceClasses() {
Set<Class<? extends PublicInterface>> result = new LinkedHashSet<Class<? extends PublicInterface>>();
for (SService sService : servicesByName.values()) {
result.add((Class<? extends PublicInterface>) sService.getInterfaceClass());
}
return result;
}
public void setReflectorFactory(ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
}
public ReflectorFactory getReflectorFactory() {
return reflectorFactory;
}
/**
* Inefficient method of getting a SMethod
*
* @param methodName
* @return
*/
public SMethod findMethod(String methodName) {
for (SService sService : servicesByName.values()) {
SMethod method = sService.getSMethod(methodName);
if (method != null) {
return method;
}
}
return null;
}
public JSONObject toJson() {
try {
JSONObject result = new JSONObject();
JSONArray servicesJson = new JSONArray();
result.put("services", servicesJson);
for (SService sService : servicesByName.values()) {
JSONObject serviceJson = new JSONObject();
serviceJson.put("name", sService.getName());
serviceJson.put("simpleName", sService.getSimpleName());
servicesJson.put(serviceJson);
JSONArray methodsJson = new JSONArray();
serviceJson.put("methods", methodsJson);
for (SMethod method : sService.getMethods()) {
methodsJson.put(method.toJson());
}
}
return result;
} catch (JSONException e) {
LOGGER.error("", e);
}
return null;
}
public void initialize() {
for (SService sService : servicesByName.values()) {
sService.init();
}
}
}