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.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.activation.DataHandler;
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;
public class SClass implements Comparable<SClass> {
private static final Logger LOGGER = LoggerFactory.getLogger(SClass.class);
private final Map<String, SField> ownFields = new TreeMap<String, SField>();
private final Map<String, SField> allFields = new TreeMap<String, SField>();
private final String name;
private final Class<?> instanceClass;
private final Set<SClass> subClasses = new TreeSet<SClass>();
private final SConstructor sConstructor;
private final SimpleType simpleType;
private SClass superClass;
private SServicesMap sServicesMap;
public SClass(SServicesMap sServicesMap, Class<?> instanceClass, SConstructor sConstructor) {
this.sServicesMap = sServicesMap;
this.sConstructor = sConstructor;
if (instanceClass == null) {
throw new RuntimeException("InstanceClass cannot be null");
}
this.instanceClass = instanceClass;
this.name = instanceClass.getName();
this.simpleType = SimpleType.get(instanceClass);
try {
Method method = instanceClass.getMethod("setSClass", new Class[]{SClass.class});
if (method != null) {
method.invoke(null, this);
}
} catch (SecurityException e) {
LOGGER.error("", e);
} catch (NoSuchMethodException e) {
} catch (IllegalArgumentException e) {
LOGGER.error("", e);
} catch (IllegalAccessException e) {
LOGGER.error("", e);
} catch (InvocationTargetException e) {
LOGGER.error("", e);
}
}
public SServicesMap getServicesMap() {
return this.sServicesMap;
}
public void init() {
for (Method method : instanceClass.getMethods()) {
if (method.getDeclaringClass() == instanceClass) {
if (method.getName().startsWith("get") && method.getName().length() > 3 && !method.getName().equals("getSClass")) {
String fieldName = StringUtils.firstLowerCase(method.getName().substring(3));
try {
if (instanceClass.getMethod("set" + StringUtils.firstUpperCase(fieldName), method.getReturnType()) != null) {
Class<?> genericType = sServicesMap.getGenericType(method);
boolean aggregate = List.class.isAssignableFrom(method.getReturnType()) || Set.class.isAssignableFrom(method.getReturnType());
SField sField = new SField(fieldName, sServicesMap.getSType(method.getReturnType().getName()), genericType == null ? null : sServicesMap.getSType(genericType.getName()), aggregate);
addField(sField);
}
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
}
if (method.getName().startsWith("is") && method.getName().length() > 2) {
String fieldName = StringUtils.firstLowerCase(method.getName().substring(2));
try {
if (instanceClass.getMethod("set" + StringUtils.firstUpperCase(fieldName), method.getReturnType()) != null) {
Class<?> genericType = sServicesMap.getGenericType(method);
boolean aggregate = List.class.isAssignableFrom(method.getReturnType()) || Set.class.isAssignableFrom(method.getReturnType());
SField sField = new SField(fieldName, sServicesMap.getSType(method.getReturnType().getName()), genericType == null ? null : sServicesMap.getSType(genericType.getName()), aggregate);
addField(sField);
}
} catch (SecurityException e) {
} catch (NoSuchMethodException e) {
}
}
}
}
Class<?> superclass = instanceClass.getSuperclass();
if (SBase.class.isAssignableFrom(instanceClass) && superclass != null) {
addSuperClass(sServicesMap.getSType(superclass.getName()));
}
}
private void addSuperClass(SClass sType) {
superClass = sType;
for (SField field : superClass.getAllFields()) {
allFields.put(field.getName(), field);
}
sType.addSubClass(this);
}
private void addSubClass(SClass sClass) {
subClasses.add(sClass);
}
public void addField(SField sField) {
ownFields.put(sField.getName(), sField);
allFields.put(sField.getName(), sField);
}
public String getName() {
return name;
}
public SClass getSuperClass() {
return superClass;
}
public Set<SClass> getSubClasses() {
return subClasses;
}
public SField getField(String name) {
return allFields.get(name);
}
public Class<?> getInstanceClass() {
return instanceClass;
}
public Collection<SField> getOwnFields() {
return ownFields.values();
}
public Collection<SField> getAllFields() {
return allFields.values();
}
public SBase newInstance() {
if (sConstructor == null) {
throw new RuntimeException("No constructor for " + getName() + "!");
}
return (SBase) sConstructor.newInstance();
}
public boolean isPrimitive() {
if (instanceClass.isPrimitive()) {
return true;
} else if (instanceClass == Long.class || instanceClass == Integer.class || instanceClass == Float.class || instanceClass == Double.class || instanceClass == Boolean.class || instanceClass == Character.class) {
return true;
} else if (instanceClass == Date.class) {
return true;
}
return false;
}
public boolean isEnum() {
return instanceClass.isEnum();
}
public List<String> getEnumValues() {
List<String> results = new ArrayList<String>();
for (Object cl : instanceClass.getEnumConstants()) {
results.add(cl.toString());
}
return results;
}
public boolean isSet() {
return instanceClass.isAssignableFrom(Set.class);
}
public boolean isString() {
return instanceClass == String.class;
}
public boolean isDate() {
return instanceClass == Date.class;
}
public boolean isClass() {
return instanceClass.isAssignableFrom(Class.class);
}
public boolean isDataHandler() {
return instanceClass == DataHandler.class;
}
public boolean isList() {
return name.equals("java.util.List");
}
public String getPrintableName() {
String name = instanceClass.getName();
if (name.startsWith("class ")) {
name = name.substring(6);
}
if (name.startsWith("interface ")) {
name = name.substring(10);
}
return name;
}
public String getSimpleName() {
return instanceClass.getSimpleName();
}
@Override
public String toString() {
return name;
}
public String toJavaCode() {
if (instanceClass == byte[].class) {
return "byte[]";
}
if (instanceClass == Byte[].class) {
return "java.lang.Byte[]";
}
return name;
}
public boolean isLong() {
return name.equals("java.lang.Long") || name.equals("long");
}
public boolean isByteArray() {
return instanceClass == byte[].class || instanceClass == Byte[].class;
}
public boolean isBoolean() {
return name.equals("java.lang.Boolean") || name.equals("boolean");
}
public boolean isDouble() {
return name.equals("java.lang.Double") || name.equals("double");
}
public boolean isFloat() {
return name.equals("java.lang.Float") || name.equals("float");
}
public boolean isInteger() {
return name.equals("java.lang.Integer") || name.equals("int");
}
public boolean isVoid() {
return name.equals("void") || name.equals("Void");
}
public SimpleType getSimpleType() {
return simpleType;
}
public JSONObject toJson() throws JSONException {
JSONObject result = new JSONObject();
result.put("name", getName());
result.put("simpleName", getSimpleName());
result.put("simpleType", getSimpleType().name());
JSONArray fieldsJson = new JSONArray();
for (SField field : ownFields.values()) {
fieldsJson.put(field.toJson());
}
result.put("fields", fieldsJson);
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((instanceClass == null) ? 0 : instanceClass.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SClass other = (SClass) obj;
if (instanceClass == null) {
if (other.instanceClass != null)
return false;
} else if (!instanceClass.equals(other.instanceClass))
return false;
return true;
}
@Override
public int compareTo(SClass arg0) {
return name.compareTo(arg0.getName());
}
}