/**
* Copyright (C) 2010-2011 J.W.Marsden
*
* 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 jsonij.json;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import jsonij.json.annotation.JSONAccessor;
import jsonij.json.annotation.JSONIgnore;
import jsonij.json.annotation.JSONMutator;
import jsonij.json.annotation.JSONName;
/**
*
* @author openecho
*/
public class Inspector {
public static final String IS_PREFIX;
public static final String SET_PREFIX;
public static final String GET_PREFIX;
Object o;
Class<?> c;
InspectorProperty[] properties;
static {
IS_PREFIX = "is";
SET_PREFIX = "set";
GET_PREFIX = "get";
}
public Inspector(Class<?> c) {
this.c = c;
this.o = null;
properties = null;
}
public Inspector(Object o) {
this.o = o;
properties = null;
}
public void inspect() {
if (o == null && c == null) {
return;
}
if (o != null) {
c = o.getClass();
}
Class<?> objectClass = c;
ArrayList<InspectorProperty> propertiesList = new ArrayList<InspectorProperty>();
InspectorProperty property = null;
Class<?> type = null;
String name = null;
String accessName = null, mutateName = null;
Method[] objectMethods = objectClass.getMethods();
for (Method method : objectMethods) {
property = new InspectorProperty();
property.setAccessType(InspectorProperty.TYPE.METHOD);
boolean nameFlag = false;
name = Inspector.getJSONName(method);
if (name != null) {
property.setName(name);
property.setUnannotatedPropertyName(name);
nameFlag = true;
}
name = Inspector.getJSONAccessor(method);
if (name != null) {
property.setAccessor(true);
property.setAccessName(method.getName());
if (!nameFlag) {
property.setName(name);
property.setUnannotatedPropertyName(name);
nameFlag = true;
}
}
name = Inspector.getJSONMutator(method);
if (name != null) {
property.setMutator(true);
property.setMutateName(method.getName());
if (!nameFlag) {
property.setName(name);
property.setUnannotatedPropertyName(name);
nameFlag = true;
}
}
type = method.getReturnType();
char c;
name = method.getName();
if (name.length() > IS_PREFIX.length() && name.startsWith(IS_PREFIX) && type == boolean.class && Character.isUpperCase(c = name.charAt(IS_PREFIX.length()))) {
name = Character.toLowerCase(c) + name.substring(IS_PREFIX.length() + 1, name.length());
if (!nameFlag) {
property.setName(name);
}
property.setAccessName(method.getName());
property.setUnannotatedPropertyName(name);
property.setAccessor(true);
} else if (name.length() > SET_PREFIX.length() && name.startsWith(SET_PREFIX) && Character.isUpperCase(c = name.charAt(SET_PREFIX.length()))) {
name = Character.toLowerCase(c) + name.substring(SET_PREFIX.length() + 1, name.length());
if (!nameFlag) {
property.setName(name);
}
property.setMutateName(method.getName());
property.setUnannotatedPropertyName(name);
property.setMutator(true);
} else if (name.length() > GET_PREFIX.length() && name.startsWith(GET_PREFIX) && Character.isUpperCase(c = name.charAt(GET_PREFIX.length())) && type != null) {
name = Character.toLowerCase(c) + name.substring(GET_PREFIX.length() + 1, name.length());
if (!nameFlag) {
property.setName(name);
}
property.setAccessName(method.getName());
property.setUnannotatedPropertyName(name);
property.setAccessor(true);
}
if (isJSONIgnored(method)) {
property.setIgnore(true);
}
if (property.getName() != null) {
boolean found = false;
for (InspectorProperty prop : propertiesList) {
if (property.getName().equals(prop.getName())) {
if (property.isAccessor() && property.getAccessName() != null) {
prop.setAccessor(true);
prop.setAccessName(property.getAccessName());
found = true;
break;
} else if (property.isMutator() && property.getMutateName() != null) {
prop.setMutator(true);
prop.setMutateName(property.getMutateName());
found = true;
break;
}
}
}
if (!found) {
propertiesList.add(property);
}
}
}
Field[] objectFields = objectClass.getFields();
for (Field field : objectFields) {
name = getJSONName(field);
if (name == null) {
name = field.getName();
}
accessName = field.getName();
mutateName = accessName;
boolean found = false;
for (InspectorProperty prop : propertiesList) {
if (prop.hasAccessorAndMutator()) {
if (name.equals(prop.getName()) || accessName.equals(prop.getUnannotatedPropertyName()) || accessName.equals(prop.getAccessName())) {
if (isJSONIgnored(field)) {
prop.setIgnore(true);
}
if(!name.equals(accessName)) {
prop.setName(name);
}
found = true;
continue;
}
}
}
if (!found) {
property = new InspectorProperty();
property.setName(name);
property.setAccessType(InspectorProperty.TYPE.FIELD);
property.setAccessName(accessName);
property.setMutateName(mutateName);
property.setUnannotatedPropertyName(accessName);
if (isJSONIgnored(field)) {
property.setIgnore(true);
}
propertiesList.add(property);
}
}
properties = new InspectorProperty[propertiesList.size()];
properties = propertiesList.toArray(properties);
}
public Object getO() {
return o;
}
public void setO(Object o) {
this.o = o;
this.properties = null;
}
public InspectorProperty[] getProperties() {
if (o != null && properties == null) {
inspect();
}
return properties;
}
public void setProperties(InspectorProperty[] properties) {
this.properties = properties;
}
public boolean isJSONIgnored(AccessibleObject object) {
return object.getAnnotation(JSONIgnore.class) != null;
}
public static String getAnnotatedName(AccessibleObject object) {
String name = getJSONName(object);
if (name == null) {
name = getJSONMutator(object);
}
if (name == null) {
name = getJSONAccessor(object);
}
return name;
}
public static String getJSONName(AccessibleObject object) {
String name = null;
JSONName jsonNameAnnotation = object.getAnnotation(JSONName.class);
if (jsonNameAnnotation != null) {
name = jsonNameAnnotation.value();
}
return name;
}
public static String getJSONMutator(AccessibleObject object) {
String name = null;
JSONMutator jsonMutatorAnnotation = object.getAnnotation(JSONMutator.class);
if (jsonMutatorAnnotation != null) {
name = jsonMutatorAnnotation.value();
}
return name;
}
public static String getJSONAccessor(AccessibleObject object) {
String name = null;
JSONAccessor jsonAccessorAnnotation = object.getAnnotation(JSONAccessor.class);
if (jsonAccessorAnnotation != null) {
name = jsonAccessorAnnotation.value();
}
return name;
}
public static class InspectorProperty {
public enum TYPE {
FIELD, METHOD
}
public String name;
public TYPE accessType;
public String accessName;
public String mutateName;
public String unannotatedPropertyName;
public boolean ignore, accessor, mutator;
public InspectorProperty() {
name = null;
accessType = null;
accessName = null;
mutateName = null;
unannotatedPropertyName = null;
ignore = false;
accessor = false;
mutator = false;
}
public InspectorProperty(String name, TYPE accessType) {
this.name = name;
this.accessType = accessType;
accessName = null;
unannotatedPropertyName = null;
ignore = false;
accessor = false;
mutator = false;
}
public TYPE getAccessType() {
return accessType;
}
public void setAccessType(TYPE accessType) {
this.accessType = accessType;
}
public String getAccessName() {
return accessName;
}
public void setAccessName(String accessName) {
this.accessName = accessName;
}
public String getMutateName() {
return mutateName;
}
public void setMutateName(String mutateName) {
this.mutateName = mutateName;
}
public String getUnannotatedPropertyName() {
return unannotatedPropertyName;
}
public void setUnannotatedPropertyName(String unannotatedPropertyName) {
this.unannotatedPropertyName = unannotatedPropertyName;
}
public boolean isIgnore() {
return ignore;
}
public void setIgnore(boolean ignore) {
this.ignore = ignore;
}
public boolean isAccessor() {
return accessor;
}
public void setAccessor(boolean accessor) {
this.accessor = accessor;
}
public boolean isMutator() {
return mutator;
}
public void setMutator(boolean mutator) {
this.mutator = mutator;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean hasAccessorAndMutator() {
return accessType == TYPE.FIELD || (mutator && accessor);
}
public boolean isValid() {
return name != null && hasAccessorAndMutator();
}
@Override
public String toString() {
return String.format("ObjectProperty %s%s (%s) valid:\"%s\" unannotated:\"%s\" (>\"%s\" <\"%s\")", ((ignore) ? "-" : "+"), name, accessType, hasAccessorAndMutator(), unannotatedPropertyName, mutateName, accessName);
}
}
}