Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2010, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.javafx.fxml.builder;
import com.sun.javafx.fxml.BeanAdapter;
import com.sun.javafx.fxml.ModuleHelper;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import javafx.beans.NamedArg;
import javafx.util.Builder;
import com.sun.javafx.reflect.ConstructorUtil;
import com.sun.javafx.reflect.ReflectUtil;
/**
* Using this builder assumes that some of the constructors of desired class
* with arguments are annotated with NamedArg annotation.
*/
public class ProxyBuilder extends AbstractMap implements Builder {
private Class type;
private final Map> constructorsMap;
private final Map propertiesMap;
private final Set constructors;
private Set propertyNames;
private boolean hasDefaultConstructor = false;
private Constructor defaultConstructor;
private static final String SETTER_PREFIX = "set";
private static final String GETTER_PREFIX = "get";
public ProxyBuilder(Class tp) {
this.type = tp;
constructorsMap = new HashMap<>();
Constructor ctors[] = ConstructorUtil.getConstructors(type);
for (Constructor c : ctors) {
Map args;
Class paramTypes[] = c.getParameterTypes();
Annotation[][] paramAnnotations = c.getParameterAnnotations();
// probably default constructor
if (paramTypes.length == 0) {
hasDefaultConstructor = true;
defaultConstructor = c;
} else { // constructor with parameters
int i = 0;
boolean properlyAnnotated = true;
args = new LinkedHashMap<>();
for (Class clazz : paramTypes) {
NamedArg argAnnotation = null;
for (Annotation annotation : paramAnnotations[i]) {
if (annotation instanceof NamedArg) {
argAnnotation = (NamedArg) annotation;
break;
}
}
if (argAnnotation != null) {
AnnotationValue av = new AnnotationValue(
argAnnotation.value(),
argAnnotation.defaultValue(),
clazz);
args.put(argAnnotation.value(), av);
} else {
properlyAnnotated = false;
break;
}
i++;
}
if (properlyAnnotated) {
constructorsMap.put(c, args);
}
}
}
if (!hasDefaultConstructor && constructorsMap.isEmpty()) {
throw new RuntimeException("Cannot create instance of "
+ type.getCanonicalName()
+ " the constructor is not properly annotated.");
}
constructors = new TreeSet<>(constructorComparator);
constructors.addAll(constructorsMap.keySet());
propertiesMap = scanForSetters();
}
//make sure int goes before float
private final Comparator constructorComparator
= (Constructor o1, Constructor o2) -> {
int len1 = o1.getParameterCount();
int len2 = o2.getParameterCount();
int lim = Math.min(len1, len2);
for (int i = 0; i < lim; i++) {
Class c1 = o1.getParameterTypes()[i];
Class c2 = o2.getParameterTypes()[i];
if (c1.equals(c2)) {
continue;
}
if (c1.equals(Integer.TYPE) && c2.equals(Double.TYPE)) {
return -1;
}
if (c1.equals(Double.TYPE) && c2.equals(Integer.TYPE)) {
return 1;
}
return c1.getCanonicalName().compareTo(c2.getCanonicalName());
}
return len1 - len2;
};
private final Map userValues = new HashMap<>();
@Override
public Object put(String key, Object value) {
userValues.put(key, value);
return null; // to behave the same way as ObjectBuilder does
}
private final Map containers = new HashMap<>();
/**
* This is used to support read-only collection property. This method must
* return a Collection of the appropriate type if 1. the property is
* read-only, and 2. the property is a collection. It must return null
* otherwise.
*
*/
private Object getTemporaryContainer(String propName) {
Object o = containers.get(propName);
if (o == null) {
o = getReadOnlyProperty(propName);
if (o != null) {
containers.put(propName, o);
}
}
return o;
}
// Wrapper for ArrayList which we use to store read-only collection
// properties in
private static class ArrayListWrapper extends ArrayList {
}
// This is used to support read-only collection property.
private Object getReadOnlyProperty(String propName) {
// return ArrayListWrapper now and convert it to proper type later
// during the build - once we know which constructor we will use
// and what types it accepts
return new ArrayListWrapper<>();
}
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public Set> entrySet() {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
return (getTemporaryContainer(key.toString()) != null);
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public Object get(Object key) {
return getTemporaryContainer(key.toString());
}
@Override
public T build() {
Object retObj = null;
// adding collection properties to userValues
for (Entry entry : containers.entrySet()) {
put(entry.getKey(), entry.getValue());
}
propertyNames = userValues.keySet();
for (Constructor c : constructors) {
Set argumentNames = getArgumentNames(c);
// the object is created only if attributes from fxml exactly match constructor arguments
if (propertyNames.equals(argumentNames)) {
retObj = createObjectWithExactArguments(c, argumentNames);
if (retObj != null) {
return (T) retObj;
}
}
}
// constructor with exact match doesn't exist
Set settersArgs = propertiesMap.keySet();
// check if all properties can be set by setters and class has default constructor
if (settersArgs.containsAll(propertyNames) && hasDefaultConstructor) {
retObj = createObjectFromDefaultConstructor();
if (retObj != null) {
return (T) retObj;
}
}
// set of mutable properties which are given by the user in fxml
Set propertiesToSet = new HashSet<>(propertyNames);
propertiesToSet.retainAll(settersArgs);
// will search for combination of constructor and setters
Set chosenConstructors = chooseBestConstructors(settersArgs);
// we have chosen the best constructors, let's try to find one we can use
for (Constructor constructor : chosenConstructors) {
retObj = createObjectFromConstructor(constructor, propertiesToSet);
if (retObj != null) {
return (T) retObj;
}
}
if (retObj == null) {
throw new RuntimeException("Cannot create instance of "
+ type.getCanonicalName() + " with given set of properties: "
+ userValues.keySet().toString());
}
return (T) retObj;
}
private Set chooseBestConstructors(Set settersArgs) {
// set of immutable properties which are given by the user in fxml
Set immutablesToSet = new HashSet<>(propertyNames);
immutablesToSet.removeAll(settersArgs);
// set of mutable properties which are given by the user in fxml
Set propertiesToSet = new HashSet<>(propertyNames);
propertiesToSet.retainAll(settersArgs);
int propertiesToSetCount = Integer.MAX_VALUE;
int mutablesToSetCount = Integer.MAX_VALUE;
// there may be more constructor with the same argument names
// (this often happens in case of List and T... etc.
Set chosenConstructors = new TreeSet<>(constructorComparator);
Set argsNotSet = null;
for (Constructor c : constructors) {
Set argumentNames = getArgumentNames(c);
// check whether this constructor takes all immutable properties
// given by the user; if not, skip it
if (!argumentNames.containsAll(immutablesToSet)) {
continue;
}
// all properties of this constructor which the user didn't
// specify in FXML
// we try to minimize this set
Set propertiesToSetInConstructor = new HashSet<>(argumentNames);
propertiesToSetInConstructor.removeAll(propertyNames);
// all mutable properties which the user did specify in FXML
// but are not settable with this constructor
// we try to minimize this too (but only if we have more constructors with
// the same propertiesToSetCount)
Set mutablesNotSet = new HashSet<>(propertiesToSet);
mutablesNotSet.removeAll(argumentNames);
int currentPropSize = propertiesToSetInConstructor.size();
if (propertiesToSetCount == currentPropSize
&& mutablesToSetCount == mutablesNotSet.size()) {
// we found constructor which is as good as the ones we already have
chosenConstructors.add(c);
}
if (propertiesToSetCount > currentPropSize
|| (propertiesToSetCount == currentPropSize && mutablesToSetCount > mutablesNotSet.size())) {
propertiesToSetCount = currentPropSize;
mutablesToSetCount = mutablesNotSet.size();
chosenConstructors.clear();
chosenConstructors.add(c);
}
}
if (argsNotSet != null && !argsNotSet.isEmpty()) {
throw new RuntimeException("Cannot create instance of "
+ type.getCanonicalName()
+ " no constructor contains all properties specified in FXML.");
}
return chosenConstructors;
}
// Returns argument names for given constructor
private Set getArgumentNames(Constructor c) {
Map constructorArgsMap = constructorsMap.get(c);
Set argumentNames = null;
if (constructorArgsMap != null) {
argumentNames = constructorArgsMap.keySet();
}
return argumentNames;
}
private Object createObjectFromDefaultConstructor() throws RuntimeException {
Object retObj = null;
// create class with default constructor and iterate over all required setters
try {
retObj = createInstance(defaultConstructor, new Object[]{});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
for (String propName : propertyNames) {
try {
Property property = propertiesMap.get(propName);
property.invoke(retObj, getUserValue(propName, property.getType()));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
return retObj;
}
private Object createObjectFromConstructor(Constructor constructor, Set propertiesToSet) {
Object retObj = null;
Map constructorArgsMap = constructorsMap.get(constructor);
Object argsForConstruction[] = new Object[constructorArgsMap.size()];
int i = 0;
// set of properties which need to be set by setters if we use current
// constructor
Set currentPropertiesToSet = new HashSet<>(propertiesToSet);
for (AnnotationValue value : constructorArgsMap.values()) {
// first try to coerce user give value
Object userValue = getUserValue(value.getName(), value.getType());
if (userValue != null) {
try {
argsForConstruction[i] = BeanAdapter.coerce(userValue, value.getType());
} catch (Exception ex) {
return null;
}
} else {
// trying to coerce default value
if (!value.getDefaultValue().isEmpty()) {
try {
argsForConstruction[i] = BeanAdapter.coerce(value.getDefaultValue(), value.getType());
} catch (Exception ex) {
return null;
}
} else {
argsForConstruction[i] = getDefaultValue(value.getType());
}
}
currentPropertiesToSet.remove(value.getName());
i++;
}
try {
retObj = createInstance(constructor, argsForConstruction);
} catch (Exception ex) {
// try next constructor
}
if (retObj != null) {
for (String propName : currentPropertiesToSet) {
try {
Property property = propertiesMap.get(propName);
property.invoke(retObj, getUserValue(propName, property.getType()));
} catch (Exception ex) {
// try next constructor
return null;
}
}
}
return retObj;
}
private Object getUserValue(String key, Class type) {
Object val = userValues.get(key);
if (val == null) {
return null;
}
if (type.isAssignableFrom(val.getClass())) {
return val;
}
// we currently don't have proper support support for arrays
// in FXML so we use lists instead
// the user provides us with a list and here we convert it to
// array to pass to the constructor
if (type.isArray()) {
try {
return convertListToArray(val, type);
} catch (RuntimeException ex) {
// conversion failed, maybe the ArrayListWrapper is
// used for storing single value
}
}
if (ArrayListWrapper.class.equals(val.getClass())) {
// user given value is an ArrayList but the constructor doesn't
// accept an ArrayList so the ArrayList comes from
// the getTemporaryContainer method
// we take the first argument
List l = (List) val;
return l.get(0);
}
return val;
}
private Object createObjectWithExactArguments(Constructor c, Set argumentNames) {
Object retObj = null;
Object argsForConstruction[] = new Object[argumentNames.size()];
Map constructorArgsMap = constructorsMap.get(c);
int i = 0;
for (String arg : argumentNames) {
Class tp = constructorArgsMap.get(arg).getType();
Object value = getUserValue(arg, tp);
try {
argsForConstruction[i++] = BeanAdapter.coerce(value, tp);
} catch (Exception ex) {
return null;
}
}
try {
retObj = createInstance(c, argsForConstruction);
} catch (Exception ex) {
// will try to fall back to different constructor
}
return retObj;
}
private Object createInstance(Constructor c, Object args[]) throws Exception {
Object retObj = null;
ReflectUtil.checkPackageAccess(type);
retObj = c.newInstance(args);
return retObj;
}
private Map scanForSetters() {
Map strsMap = new HashMap<>();
Map> methods = getClassMethodCache(type);
for (String methodName : methods.keySet()) {
if (methodName.startsWith(SETTER_PREFIX) && methodName.length() > SETTER_PREFIX.length()) {
String propName = methodName.substring(SETTER_PREFIX.length());
propName = Character.toLowerCase(propName.charAt(0)) + propName.substring(1);
List methodsList = methods.get(methodName);
for (Method m : methodsList) {
Class retType = m.getReturnType();
Class argType[] = m.getParameterTypes();
if (retType.equals(Void.TYPE) && argType.length == 1) {
strsMap.put(propName, new Setter(m, argType[0]));
}
}
}
if (methodName.startsWith(GETTER_PREFIX) && methodName.length() > GETTER_PREFIX.length()) {
String propName = methodName.substring(GETTER_PREFIX.length());
propName = Character.toLowerCase(propName.charAt(0)) + propName.substring(1);
List methodsList = methods.get(methodName);
for (Method m : methodsList) {
Class retType = m.getReturnType();
Class argType[] = m.getParameterTypes();
if (Collection.class.isAssignableFrom(retType) && argType.length == 0) {
strsMap.put(propName, new Getter(m, retType));
}
}
}
}
return strsMap;
}
private static abstract class Property {
protected final Method method;
protected final Class type;
public Property(Method m, Class t) {
method = m;
type = t;
}
public Class getType() {
return type;
}
public abstract void invoke(Object obj, Object argStr) throws Exception;
}
private static class Setter extends Property {
public Setter(Method m, Class t) {
super(m, t);
}
public void invoke(Object obj, Object argStr) throws Exception {
Object arg[] = new Object[]{BeanAdapter.coerce(argStr, type)};
ModuleHelper.invoke(method, obj, arg);
}
}
private static class Getter extends Property {
public Getter(Method m, Class t) {
super(m, t);
}
@Override
public void invoke(Object obj, Object argStr) throws Exception {
// we know that this.method returns collection otherwise it wouldn't be here
Collection to = (Collection) ModuleHelper.invoke(method, obj, new Object[]{});
if (argStr instanceof Collection) {
Collection from = (Collection) argStr;
to.addAll(from);
} else {
to.add(argStr);
}
}
}
// This class holds information for one argument of the constructor
// which we got from the NamedArg annotation
private static class AnnotationValue {
private final String name;
private final String defaultValue;
private final Class type;
public AnnotationValue(String name, String defaultValue, Class type) {
this.name = name;
this.defaultValue = defaultValue;
this.type = type;
}
public String getName() {
return name;
}
public String getDefaultValue() {
return defaultValue;
}
public Class getType() {
return type;
}
}
private static HashMap> getClassMethodCache(Class type) {
HashMap> classMethodCache = new HashMap<>();
ReflectUtil.checkPackageAccess(type);
Method[] declaredMethods = type.getMethods();
for (Method method : declaredMethods) {
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
String name = method.getName();
LinkedList namedMethods = classMethodCache.get(name);
if (namedMethods == null) {
namedMethods = new LinkedList<>();
classMethodCache.put(name, namedMethods);
}
namedMethods.add(method);
}
}
return classMethodCache;
}
// Utility method for converting list to array via reflection
// it assumes that localType is array
private static Object[] convertListToArray(Object userValue, Class localType) {
Class arrayType = localType.getComponentType();
List l = (List) BeanAdapter.coerce(userValue, List.class);
return l.toArray((Object[]) Array.newInstance(arrayType, 0));
}
private static Object getDefaultValue(Class clazz) {
return DEFAULTS_MAP.get(clazz);
}
private static final Map, Object> DEFAULTS_MAP = new HashMap<>(9);
static {
DEFAULTS_MAP.put(byte.class, (byte) 0);
DEFAULTS_MAP.put(short.class, (short) 0);
DEFAULTS_MAP.put(int.class, 0);
DEFAULTS_MAP.put(long.class, 0L);
DEFAULTS_MAP.put(float.class, 0.0f);
DEFAULTS_MAP.put(double.class, 0.0d);
DEFAULTS_MAP.put(char.class, '\u0000');
DEFAULTS_MAP.put(boolean.class, false);
DEFAULTS_MAP.put(Object.class, null);
}
}