package org.netbeans.gradle.model.internal;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicReference;
import org.netbeans.gradle.model.api.ProjectInfoBuilder2;
import org.netbeans.gradle.model.util.ReflectionUtils;
public final class ConstrProjectInfoBuilderRef<T> implements ProjectInfoBuilder2<T>, BuilderWrapper {
private static final long serialVersionUID = 1L;
private final Class<? extends T> modelType;
private final String wrappedTypeName;
private final Object[] arguments;
private final AtomicReference<ProjectInfoBuilder2<?>> wrappedRef;
public ConstrProjectInfoBuilderRef(Class<? extends T> modelType, String wrappedTypeName, Object[] arguments) {
if (modelType == null) throw new NullPointerException("modelType");
if (wrappedTypeName == null) throw new NullPointerException("wrappedTypeName");
this.modelType = modelType;
this.wrappedTypeName = ReflectionUtils.updateTypeName(modelType, wrappedTypeName);
this.arguments = arguments.clone();
this.wrappedRef = new AtomicReference<ProjectInfoBuilder2<?>>(null);
}
private static boolean isApplicable(Class<?>[] parameterTypes, Object[] arguments) {
if (parameterTypes.length != arguments.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
Object arg = arguments[i];
if (arg != null && !parameterTypes[i].isInstance(arg)) {
return false;
}
}
return true;
}
private RuntimeException rethrow(Throwable ex) {
if (ex instanceof InvocationTargetException) {
return rethrow(ex.getCause());
}
if (ex instanceof RuntimeException) {
throw (RuntimeException)ex;
}
if (ex instanceof Error) {
throw (Error)ex;
}
throw new RuntimeException(ex);
}
private ProjectInfoBuilder2<?> getWrapped() {
ProjectInfoBuilder2<?> result = wrappedRef.get();
if (result == null) {
result = createWrapped();
if (!wrappedRef.compareAndSet(null, result)) {
result = wrappedRef.get();
}
}
return result;
}
private ProjectInfoBuilder2<?> createWrapped() {
try {
Class<?> wrappedType = Class.forName(wrappedTypeName, false, modelType.getClassLoader());
for (Constructor<?> constr: wrappedType.getConstructors()) {
if (isApplicable(constr.getParameterTypes(), arguments)) {
return (ProjectInfoBuilder2<?>)constr.newInstance(arguments);
}
}
} catch (Exception ex) {
throw rethrow(ex);
}
throw new IllegalStateException("Cannot find appropriate constructor for " + wrappedTypeName);
}
public T getProjectInfo(Object project) {
Object result = getWrapped().getProjectInfo(project);
return modelType.cast(result);
}
public String getName() {
return getWrapped().getName();
}
public Object getWrappedObject() {
return null;
}
public Class<?> getWrappedType() {
return modelType;
}
private Object writeReplace() {
return new SerializedFormat(this);
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Use proxy.");
}
private static final class SerializedFormat implements Serializable {
private static final long serialVersionUID = 1L;
private final Class<?> modelType;
private final String wrappedTypeName;
private final Object[] arguments;
public SerializedFormat(ConstrProjectInfoBuilderRef<?> source) {
this.modelType = source.modelType;
this.wrappedTypeName = source.wrappedTypeName;
this.arguments = source.arguments;
}
private Object readResolve() throws ObjectStreamException {
return new ConstrProjectInfoBuilderRef<Object>(modelType, wrappedTypeName, arguments);
}
}
}