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.
/*
* The MIT License (MIT)
*
* Copyright (c) 2015-2024 Stephan Pauxberger
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.blackbuild.klum.ast.util;
import com.blackbuild.annodocimal.annotations.InlineJavadocs;
import com.blackbuild.groovy.configdsl.transform.*;
import com.blackbuild.klum.ast.process.BreadcrumbCollector;
import groovy.lang.*;
import groovy.transform.Undefined;
import org.codehaus.groovy.reflection.CachedField;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.InvokerHelper;
import java.lang.reflect.Field;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.Collectors;
import static com.blackbuild.klum.ast.util.DslHelper.*;
import static groovyjarjarasm.asm.Opcodes.*;
import static java.lang.String.format;
/**
* Implementations for generated instance methods.
*/
@SuppressWarnings("unused") // called from generated code
@InlineJavadocs
public class KlumInstanceProxy {
public static final String NAME_OF_RW_FIELD_IN_MODEL_CLASS = "$rw";
public static final String NAME_OF_PROXY_FIELD_IN_MODEL_CLASS = "$proxy";
public static final Class FIELD_ANNOTATION = com.blackbuild.groovy.configdsl.transform.Field.class;
private final GroovyObject instance;
private boolean manualValidation;
public KlumInstanceProxy(GroovyObject instance) {
this.instance = instance;
}
/**
* Returns the proxy for a given dsl object. Throws an illegalArgumentException if target is not a dsl object.
* @param target the target object
* @return the proxy instance of the given target.
*/
public static KlumInstanceProxy getProxyFor(Object target) {
if (!isDslObject(target))
throw new IllegalArgumentException(format("Object of type %s is no dsl object", target.getClass()));
return (KlumInstanceProxy) InvokerHelper.getAttribute(target, KlumInstanceProxy.NAME_OF_PROXY_FIELD_IN_MODEL_CLASS);
}
protected GroovyObject getRwInstance() {
return (GroovyObject) InvokerHelper.getAttribute(instance, KlumInstanceProxy.NAME_OF_RW_FIELD_IN_MODEL_CLASS);
}
public Object getDSLInstance() {
return instance;
}
// TODO: protected/private
public T getInstanceAttribute(String attributeName) {
return (T) getCachedField(attributeName).getProperty(instance);
}
public T getInstanceAttributeOrGetter(String attributeName) {
Optional field = DslHelper.getCachedField(instance.getClass(), attributeName);
if (field.isPresent())
return (T) field.get().getProperty(instance);
return (T) InvokerHelper.getProperty(instance, attributeName);
}
void setInstanceAttribute(String name, Object value) {
getCachedField(name).setProperty(instance, value);
}
// TODO: private?
public Object getInstanceProperty(String name){
return makeReadOnly(getInstanceAttributeOrGetter(name));
}
private T makeReadOnly(T value) {
if (value instanceof EnumSet)
return (T) EnumSet.copyOf((EnumSet>) value);
if (value instanceof Collection || value instanceof Map)
return (T) InvokerHelper.invokeMethod(DefaultGroovyMethods.class, "asImmutable", value);
return value;
}
Field getField(String name) {
return DslHelper.getField(instance.getClass(), name)
.orElseThrow(() -> new MissingPropertyException(name, instance.getClass()));
}
CachedField getCachedField(String name) {
return DslHelper.getCachedField(instance.getClass(), name)
.orElseThrow(() -> new MissingPropertyException(name, instance.getClass()));
}
/**
* Applies the given named params and the closure to this proxy's object.
* Both params are optional. The map will be converted into a series of method calls, with the key being the method name and the value the single method argument.
* The closure will be executed against the instance's RW object.
*
Note that explicit calls to apply() are usually not necessary, as apply is part of the creation of an object.
* @param values Map of String to Object which will be translated into Method calls
* @param body Closure to be executed against the instance.
* @return the object itself
*/
public Object apply(Map values, Closure> body) {
applyOnly(values, body);
LifecycleHelper.executeLifecycleMethods(this, PostApply.class);
return instance;
}
void applyOnly(Map values, Closure> body) {
Object rw = instance.getProperty(NAME_OF_RW_FIELD_IN_MODEL_CLASS);
applyNamedParameters(rw, values);
applyClosure(rw, body);
}
private void applyClosure(Object rw, Closure> body) {
if (body == null) return;
body.setDelegate(rw);
body.setResolveStrategy(Closure.DELEGATE_ONLY);
body.call();
}
private void applyNamedParameters(Object rw, Map values) {
if (values == null) return;
values.forEach((key, value) -> InvokerHelper.invokeMethod(rw, key, value));
}
/**
* Copies all non null / non empty elements from target to this.
* @param template The template to apply
*/
public void copyFrom(Object template) {
DslHelper.getDslHierarchyOf(instance.getClass()).forEach(it -> copyFromLayer(it, template));
}
public Object cloneInstance() {
Object key = isKeyed(instance.getClass()) ? getKey() : null;
Object result = FactoryHelper.createInstance(instance.getClass(), (String) key);
getProxyFor(result).copyFrom(instance);
return result;
}
private void copyFromLayer(Class> layer, Object template) {
if (layer.isInstance(template))
Arrays.stream(layer.getDeclaredFields())
.filter(this::isNotIgnored)
.forEach(field -> copyFromField(field, template));
}
private boolean isIgnored(Field field) {
if ((field.getModifiers() & (ACC_SYNTHETIC | ACC_FINAL | ACC_TRANSIENT)) != 0) return true;
if (field.isAnnotationPresent(Key.class)) return true;
if (field.isAnnotationPresent(Owner.class)) return true;
if (field.isAnnotationPresent(Role.class)) return true;
if (field.getName().startsWith("$")) return true;
if (DslHelper.getKlumFieldType(field) == FieldType.TRANSIENT) return true;
return false;
}
private boolean isNotIgnored(Field field) {
return !isIgnored(field);
}
private void copyFromField(Field field, Object template) {
String fieldName = field.getName();
Object templateValue = getProxyFor(template).getInstanceAttribute(fieldName);
if (templateValue == null) return;
if (templateValue instanceof Collection)
copyFromCollectionField((Collection>) templateValue, fieldName);
else if (templateValue instanceof Map)
copyFromMapField((Map,?>) templateValue, fieldName);
else
setInstanceAttribute(fieldName, getCopiedValue(templateValue));
}
@SuppressWarnings("unchecked")
private T getCopiedValue(T templateValue) {
if (isDslType(templateValue.getClass()))
return (T) getProxyFor(templateValue).cloneInstance();
else if (templateValue instanceof Collection)
return (T) createCopyOfCollection((Collection>) templateValue);
else if (templateValue instanceof Map)
return (T) createCopyOfMap((Map) templateValue);
else
return templateValue;
}
private Collection createCopyOfCollection(Collection templateValue) {
Collection result = createNewEmptyCollectionOrMapFrom(templateValue);
templateValue.stream().map(this::getCopiedValue).forEach(result::add);
return result;
}
private Map createCopyOfMap(Map templateValue) {
Map result = createNewEmptyCollectionOrMapFrom(templateValue);
templateValue.forEach((key, value) -> result.put(key, getCopiedValue(value)));
return result;
}
@SuppressWarnings("unchecked")
private static T createNewEmptyCollectionOrMapFrom(T source) {
return (T) InvokerHelper.invokeConstructorOf(source.getClass(), null);
}
private void copyFromMapField(Map templateValue, String fieldName) {
if (templateValue.isEmpty()) return;
Map instanceField = getInstanceAttribute(fieldName);
instanceField.clear();
templateValue.forEach((k, v) -> instanceField.put(k, getCopiedValue(v)));
}
private void copyFromCollectionField(Collection templateValue, String fieldName) {
if (templateValue.isEmpty()) return;
Collection instanceField = getInstanceAttribute(fieldName);
instanceField.clear();
templateValue.stream().map(this::getCopiedValue).forEach(instanceField::add);
}
/**
* Returns the key of this proxies instance. Illegal to call on an non keyed instance.
* @return The key
*/
Object getKey() {
return DslHelper.getKeyField(instance.getClass())
.map(Field::getName)
.map(instance::getProperty)
.orElseThrow(AssertionError::new);
}
/**
* Executes validation for this instance
*
* @deprecated use {@link com.blackbuild.klum.ast.util.Validator#validate(Object)} instead
*/
@Deprecated(forRemoval = true)
public void validate() {
Validator.validate(instance);
}
boolean getManualValidation() {
return manualValidation;
}
void manualValidation() {
manualValidation = true;
}
void manualValidation(boolean value) {
manualValidation = value;
}
/**
* Returns the owner of this object. If the object has more than one field annotated with {@link Owner},
* all of them that are not null must point to the same object, otherwise an {@link IllegalStateException}
* is thrown.
* @return The found owner or null
*/
public Object getSingleOwner() {
Set