http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaClassRegistry.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaClassRegistry.java b/src/main/groovy/groovy/lang/MetaClassRegistry.java new file mode 100644 index 0000000..f13c1c1 --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaClassRegistry.java @@ -0,0 +1,192 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.runtime.GeneratedClosure; +import org.codehaus.groovy.runtime.metaclass.ClosureMetaClass; + +import java.lang.reflect.Constructor; +import java.util.Iterator; + +/** + * A MetaClassRegistry is an object that is responsible for managing the a cache of MetaClass instances. Each + * java.lang.Class instance has an associated MetaClass and client code can query this interface for the MetaClass for + * a given associated java.lang.Class + * + * @see groovy.lang.MetaClass + * + * @author John Wilson + * @author Graeme Rocher + * @author <a href="mailto:[email protected]">Jochen Theodorou</a> + * + */ +public interface MetaClassRegistry { + + /** + * The main function of the registry + * If a meta class exists then return it + * otherwise create one, put it in the registry and return it + */ + MetaClass getMetaClass(Class theClass); + + /** + * Adds a metaclass to the registry for the given class + * + * @param theClass The class + * @param theMetaClass The MetaClass for theClass + */ + void setMetaClass(Class theClass, MetaClass theMetaClass); + + /** + * Removes a cached MetaClass from the registry + * + * @param theClass The Java class of the MetaClass to remove + */ + void removeMetaClass(Class theClass); + + /** + * Retrieves the MetaClassCreationHandle that is responsible for constructing MetaClass instances + * + * @return The MetaClassCreationHandle instance + */ + MetaClassCreationHandle getMetaClassCreationHandler(); + + /** + * Sets the MetaClassCreationHandle instance that is responsible for constructing instances + * + * @param handle The handle instance + */ + void setMetaClassCreationHandle(MetaClassCreationHandle handle); + + /** + * Adds a meta class change listener for constant meta classes + * + * @param listener - the update listener + */ + void addMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener); + + /** + * Adds a meta class change listener for constant meta classes. + * This listener cannot be removed! + * + * @param listener - the update listener + */ + void addNonRemovableMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener); + + /** + * Removes a meta class change listener for constant meta classes + * + * @param listener - the update listener + */ + void removeMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener); + + /** + * Returns all registered class change listener for constant meta classes. + * + * @return an array containing all change listener + */ + MetaClassRegistryChangeEventListener[] getMetaClassRegistryChangeEventListeners(); + + /** + * Gets a snapshot of the current constant meta classes and returns it as Iterator. + * Modifications done using this Iterator will not cause a ConcurrentModificationException. + * If a MetaClass is removed using this Iterator, then the MetaClass will only + * be removed if the MetaClass was not replaced by another MetaClass in the meantime. + * If a MetaClass is added while using this Iterator, then it will be part of the Iteration. + * If a MetaClass replaces another constant meta class, then the Iteration might show two + * meta classes for the same class. + * <p> + * Note: This Iterator may not used with multiple threads. + * + * @return Iterator for the constant meta classes + */ + Iterator iterator(); + + /** + * Class used as base for the creation of MetaClass implementations. + * The Class defaults to MetaClassImpl, if the class loading fails to + * find a special meta class. The name for such a meta class would be + * the class name it is created for with the prefix + * "groovy.runtime.metaclass." By replacing the handle in the registry + * you can have any control over the creation of what MetaClass is used + * for a class that you want to have. + * WARNING: experimental code, likely to change soon + * @author Jochen Theodorou + */ + class MetaClassCreationHandle { + private boolean disableCustomMetaClassLookup; + + /** + * Creates a metaclass implementation for theClass. + * @param theClass The class to create a metaclass for + * @param registry The metaclass registry the metaclass we be registered in. + */ + public final MetaClass create(Class theClass, MetaClassRegistry registry) { + if (disableCustomMetaClassLookup) + return createNormalMetaClass(theClass, registry); + + return createWithCustomLookup(theClass, registry); + } + + private MetaClass createWithCustomLookup(Class theClass, MetaClassRegistry registry) { + try { + final Class customMetaClass = Class.forName("groovy.runtime.metaclass." + theClass.getName() + "MetaClass"); + if (DelegatingMetaClass.class.isAssignableFrom(customMetaClass)) { + final Constructor customMetaClassConstructor = customMetaClass.getConstructor(MetaClass.class); + MetaClass normalMetaClass = createNormalMetaClass(theClass, registry); + return (MetaClass)customMetaClassConstructor.newInstance(normalMetaClass); + } + else { + final Constructor customMetaClassConstructor = customMetaClass.getConstructor(MetaClassRegistry.class, Class.class); + return (MetaClass)customMetaClassConstructor.newInstance(registry, theClass); + } + } + catch (final ClassNotFoundException e) { + return createNormalMetaClass(theClass, registry); + } catch (final Exception e) { + throw new GroovyRuntimeException("Could not instantiate custom Metaclass for class: " + theClass.getName() + ". Reason: " + e, e); + } + } + + protected MetaClass createNormalMetaClass(Class theClass,MetaClassRegistry registry) { + if (GeneratedClosure.class.isAssignableFrom(theClass)) { + return new ClosureMetaClass(registry,theClass); + } else { + return new MetaClassImpl(registry, theClass); + } + } + + /** + * Returns whether custom meta classes are disabled. + */ + public boolean isDisableCustomMetaClassLookup() { + return disableCustomMetaClassLookup; + } + + /** + * Set flag saying to disable lookup of custom meta classes + * It's enough to call this method only once in your application for handle which was set in to registry + * as every new handle will inherit this property + * @param disableCustomMetaClassLookup flag saying to disable lookup of custom meta classes + */ + public void setDisableCustomMetaClassLookup(boolean disableCustomMetaClassLookup) { + this.disableCustomMetaClassLookup = disableCustomMetaClassLookup; + } + } + }
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaClassRegistryChangeEvent.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaClassRegistryChangeEvent.java b/src/main/groovy/groovy/lang/MetaClassRegistryChangeEvent.java new file mode 100644 index 0000000..c882fda --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaClassRegistryChangeEvent.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import java.util.EventObject; + +/** + * An event used to propagate meta class updates + * + * @author <a href="mailto:[email protected]">Jochen Theodorou</a> + */ +public class MetaClassRegistryChangeEvent extends EventObject { + private final Class clazz; + private final Object instance; + private final MetaClass metaClass; + private final MetaClass oldMetaClass; + + /** + *Constructs a new MetaClassRegistryChangeEvent Object + * + * @param source The object the the event originates at. + * @param instance Object instance the MetaClass change is on. + * @param clazz The class that is affected by the registry change + * @param oldMetaClass The old MetaClass + * @param newMetaClass The new MetaClass + */ + public MetaClassRegistryChangeEvent(Object source, Object instance, Class clazz, MetaClass oldMetaClass, MetaClass newMetaClass) { + super(source); + this.clazz = clazz; + this.metaClass = newMetaClass; + this.oldMetaClass = oldMetaClass; + this.instance = instance; + } + + /** + * Get the class that is updated. + * + *@return The updated class + */ + public Class getClassToUpdate() { + return clazz; + } + + /** + * Get the new MetaClass + * + * @return The new MetaClass + */ + public MetaClass getNewMetaClass() { + return metaClass; + } + + /** + * Get the old MetaClass + * + * @return The old MetaClass + */ + public MetaClass getOldMetaClass() { + return oldMetaClass; + } + + /** + * Determines if this event is for a change for a single instance or all instances of the Class. + * + * @return whether this event is for a single instance + */ + public boolean isPerInstanceMetaClassChange() { + return instance!=null; + } + + /** + * Returns the instance this event is for. + * + * @return the instance or null if this event is for a change for all instances of a class + */ + public Object getInstance() { + return instance; + } + + /** + * Get the MetaClassRegistry that originates this change + * + * @return the source MetaClassRegistry + */ + public MetaClassRegistry getRegistry() { + return (MetaClassRegistry) source; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaClassRegistryChangeEventListener.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaClassRegistryChangeEventListener.java b/src/main/groovy/groovy/lang/MetaClassRegistryChangeEventListener.java new file mode 100644 index 0000000..e7fd40c --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaClassRegistryChangeEventListener.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import java.util.EventListener; + +/** + * A listener called whenever a constant MetaClass is set, removed or replaced. + * + * @see groovy.lang.MetaClassRegistry + * @see groovy.lang.MetaClassRegistryChangeEvent + * + * @author <a href="mailto:[email protected]">Jochen Theodorou</a> + * + */ +public interface MetaClassRegistryChangeEventListener extends EventListener{ + + /** + * Called when the a constant MetaClass is updated. If the new MetaClass is null, then the MetaClass + * is removed. Be careful, while this method is executed other updates may happen. If you want this + * method thread safe, you have to take care of that by yourself. + * + * @param cmcu - the change event + */ + void updateConstantMetaClass(MetaClassRegistryChangeEvent cmcu); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaExpandoProperty.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaExpandoProperty.java b/src/main/groovy/groovy/lang/MetaExpandoProperty.java new file mode 100644 index 0000000..6eb2cf7 --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaExpandoProperty.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import java.util.Map.Entry; + +/** + * Represents a property in an Expando object + * + * @author John Stump + */ +public class MetaExpandoProperty extends MetaProperty { + + Object value = null; + + public MetaExpandoProperty(Entry entry) { + super((String) entry.getKey(), Object.class); + + value = entry.getValue(); + } + + /** + * @return the property of the given object + * @throws Exception if the property could not be evaluated + */ + public Object getProperty(Object object) { + return value; + } + + /** + * Sets the property on the given object to the new value + * + * @param object on which to set the property + * @param newValue the new value of the property + */ + public void setProperty(Object object, Object newValue) { + value = newValue; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaMethod.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaMethod.java b/src/main/groovy/groovy/lang/MetaMethod.java new file mode 100644 index 0000000..3051675 --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaMethod.java @@ -0,0 +1,330 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.classgen.asm.BytecodeHelper; +import org.codehaus.groovy.reflection.CachedClass; +import org.codehaus.groovy.reflection.ParameterTypes; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.codehaus.groovy.runtime.MetaClassHelper; + +import java.lang.reflect.Modifier; + +/** + * Represents a Method on a Java object a little like {@link java.lang.reflect.Method} + * except without using reflection to invoke the method + * + * @author <a href="mailto:[email protected]">James Strachan</a> + * @author Alex Tkachman + */ +public abstract class MetaMethod extends ParameterTypes implements Cloneable { + private String signature; + private String mopName; + + /** + * Constructor for a metamethod with an empty parameter list + */ + public MetaMethod() { + } + + /** + *Constructor wit a list of parameter classes + * + * @param pt A list of parameters types + */ + public MetaMethod(Class [] pt) { + super (pt); + } + + /** + *Returns the modifiers for this method + * + * @return modifiers as an int. + */ + public abstract int getModifiers(); + + /** + * Returns the name of the method represented by this class + * + * @return name of this method + */ + public abstract String getName(); + + /** + * Access the return type for this method + * + *@return the return type of this method + */ + public abstract Class getReturnType(); + + /** + * Gets the class where this method is declared + * + * @return class of this method + */ + public abstract CachedClass getDeclaringClass(); + + /** + * Invoke this method + * + * @param object The object this method should be invoked on + * @param arguments The arguments for the method if applicable + * @return The return value of the invocation + */ + public abstract Object invoke(Object object, Object[] arguments); + + /** + * Checks that the given parameters are valid to call this method + * + * @param arguments the arguments to check + * @throws IllegalArgumentException if the parameters are not valid + */ + public void checkParameters(Class[] arguments) { + // lets check that the argument types are valid + if (!isValidMethod(arguments)) { + throw new IllegalArgumentException( + "Parameters to method: " + + getName() + + " do not match types: " + + InvokerHelper.toString(getParameterTypes()) + + " for arguments: " + + InvokerHelper.toString(arguments)); + } + } + + /** + *Returns true if this this metamethod represents the same method as the argument. + * + * @param method A metaMethod instance + * @return true if method is for the same method as this method, false otherwise. + */ + public boolean isMethod(MetaMethod method) { + return getName().equals(method.getName()) + && getModifiers() == method.getModifiers() + && getReturnType().equals(method.getReturnType()) + && equal(getParameterTypes(), method.getParameterTypes()); + } + + protected static boolean equal(CachedClass[] a, Class[] b) { + if (a.length == b.length) { + for (int i = 0, size = a.length; i < size; i++) { + if (!a[i].getTheClass().equals(b[i])) { + return false; + } + } + return true; + } + return false; + } + + protected static boolean equal(CachedClass[] a, CachedClass[] b) { + if (a.length == b.length) { + for (int i = 0, size = a.length; i < size; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + return false; + } + + /** + * Returns a string representation of this method + */ + public String toString() { + return super.toString() + + "[name: " + + getName() + + " params: " + + InvokerHelper.toString(getParameterTypes()) + + " returns: " + + getReturnType() + + " owner: " + + getDeclaringClass() + + "]"; + } + + public Object clone() { + try { + return super.clone(); + } + catch (CloneNotSupportedException e) { + throw new GroovyRuntimeException("This should never happen", e); + } + } + + /** + * Returns whether or not this method is static. + * @return true if this method is static + */ + public boolean isStatic() { + return (getModifiers() & Modifier.STATIC) != 0; + } + + /** + * Returns whether or not this method is abstract. + * @return true if this method is abstract + */ + public boolean isAbstract() { + return (getModifiers() & Modifier.ABSTRACT) != 0; + } + + /** + * Returns whether or not this method is private. + * @return true if this method is private + */ + public final boolean isPrivate() { + return (getModifiers() & Modifier.PRIVATE) != 0; + } + + /** + * Returns whether or not this method is protected. + * @return true if this method is protected + */ + public final boolean isProtected() { + return (getModifiers() & Modifier.PROTECTED) != 0; + } + + /** + * Returns whether or not this method is public. + * @return true if this method is public + */ + public final boolean isPublic() { + return (getModifiers() & Modifier.PUBLIC) != 0; + } + + /** + * @param method the method to compare against + * @return true if the given method has the same name, parameters, return type + * and modifiers but may be defined on another type + */ + public final boolean isSame(MetaMethod method) { + return getName().equals(method.getName()) + && compatibleModifiers(getModifiers(), method.getModifiers()) + && getReturnType().equals(method.getReturnType()) + && equal(getParameterTypes(), method.getParameterTypes()); + } + + /** + * Checks the compatibility between two modifier masks. Checks that they are equal + * with regards to access and static modifier. + * + * @return true if the modifiers are compatible + */ + private static boolean compatibleModifiers(int modifiersA, int modifiersB) { + int mask = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC | Modifier.STATIC; + return (modifiersA & mask) == (modifiersB & mask); + } + + /** + * Returns whether this object is cacheable + */ + public boolean isCacheable() { + return true; + } + + /** + * Return a descriptor of this method based on the return type and parameters of this method. + */ + public String getDescriptor() { + return BytecodeHelper.getMethodDescriptor(getReturnType(), getNativeParameterTypes()); + } + + /** + * Returns the signature of this method + * + * @return The signature of this method + */ + public synchronized String getSignature() { + if (signature == null) { + CachedClass [] parameters = getParameterTypes(); + final String name = getName(); + StringBuilder buf = new StringBuilder(name.length()+parameters.length*10); + buf.append(getReturnType().getName()); + + buf.append(' '); + buf.append(name); + buf.append('('); + for (int i = 0; i < parameters.length; i++) { + if (i > 0) { + buf.append(", "); + } + buf.append(parameters[i].getName()); + } + buf.append(')'); + signature = buf.toString(); + } + return signature; + } + + + public String getMopName() { + if (mopName == null) { + String name = getName(); + CachedClass declaringClass = getDeclaringClass(); + if (Modifier.isPrivate(getModifiers())) + mopName = new StringBuffer().append("this$").append(declaringClass.getSuperClassDistance()).append("$").append(name).toString(); + else + mopName = new StringBuffer().append("super$").append(declaringClass.getSuperClassDistance()).append("$").append(name).toString(); + } + return mopName; + } + + /** + * This method is called when an exception occurs while invoking this method. + */ + public final RuntimeException processDoMethodInvokeException (Exception e, Object object, Object [] argumentArray) { +// if (e instanceof IllegalArgumentException) { +// //TODO: test if this is OK with new MOP, should be changed! +// // we don't want the exception being unwrapped if it is a IllegalArgumentException +// // but in the case it is for example a IllegalThreadStateException, we want the unwrapping +// // from the runtime +// //Note: the reason we want unwrapping sometimes and sometimes not is that the method +// // invocation tries to invoke the method with and then reacts with type transformation +// // if the invocation failed here. This is OK for IllegalArgumentException, but it is +// // possible that a Reflector will be used to execute the call and then an Exception from inside +// // the method is not wrapped in a InvocationTargetException and we will end here. +// boolean setReason = e.getClass() != IllegalArgumentException.class || this instanceof org.codehaus.groovy.reflection.GeneratedMetaMethod; +// return MetaClassHelper.createExceptionText("failed to invoke method: ", this, object, argumentArray, e, setReason); +// } + + if (e instanceof RuntimeException) + return (RuntimeException) e; + + return MetaClassHelper.createExceptionText("failed to invoke method: ", this, object, argumentArray, e, true); + } + + /** + * Invokes the method this object represents. This method is not final but it should be overloaded very carefully and only by generated methods + * there is no guarantee that it will be called + * + * @param object The object the method is to be called at. + * @param argumentArray Arguments for the method invocation. + * @return The return value of the invoked method. + */ + public Object doMethodInvoke(Object object, Object[] argumentArray) { + argumentArray = coerceArgumentsToClasses(argumentArray); + try { + return invoke(object, argumentArray); + } catch (Exception e) { + throw processDoMethodInvokeException(e, object, argumentArray); + } + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaObjectProtocol.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaObjectProtocol.java b/src/main/groovy/groovy/lang/MetaObjectProtocol.java new file mode 100644 index 0000000..4218831 --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaObjectProtocol.java @@ -0,0 +1,227 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import java.util.List; + +/** + * <p>An interface that defines the API usable by clients of Groovy's Meta Object Protocol (MOP). These methods are + * implemented by the reference implementation of the {@link groovy.lang.MetaClass} interface. + * + * @see MetaClassImpl + * @author John Wilson + * @author Graeme Rocher + */ +public interface MetaObjectProtocol { + + /** + * Obtain a list of all meta properties available on this meta class + * + * @see groovy.lang.MetaProperty + * @return A list of MetaProperty instances + */ + List<MetaProperty> getProperties(); + /** + * Obtain a list of all the meta methods available on this meta class + * + * @see groovy.lang.MetaMethod + * @return A list of MetaMethod instances + */ + List<MetaMethod> getMethods(); + + /** + * <p>Returns an object satisfying Groovy truth if the implementing MetaClass responds to + * a method with the given name and arguments types. + * + * <p>Note that this method's return value is based on realised methods and does not take into account + * objects or classes that implement invokeMethod or methodMissing + * + * <p>This method is "safe" in that it will always return a value and never throw an exception + * + * @param obj The object to inspect + * @param name The name of the method of interest + * @param argTypes The argument types to match against + * @return A List of MetaMethods matching the argument types which will be empty if no matching methods exist + */ + List<MetaMethod> respondsTo(Object obj, String name, Object[] argTypes); + + /** + * <p>Returns an object satisfying Groovy truth if the implementing MetaClass responds to + * a method with the given name regardless of arguments. In other words this method will + * return for foo() and foo(String). + * + * <p>Note that this method's return value is based on realised methods and does not take into account + * objects or classes that implement invokeMethod or methodMissing + * + * <p>This method is "safe" in that it will always return a value and never throw an exception + * + * @param obj The object to inspect + * @param name The name of the method of interest + * @return A List of MetaMethods which will be empty if no methods with the given name exist + */ + List<MetaMethod> respondsTo(Object obj, String name); + + /** + * <p>Returns true of the implementing MetaClass has a property of the given name + * + * <p>Note that this method will only return true for realised properties and does not take into + * account implementation of getProperty or propertyMissing + * + * @param obj The object to inspect + * @param name The name of the property + * @return The MetaProperty or null if it doesn't exist + */ + MetaProperty hasProperty(Object obj, String name); + + /** + * Returns a MetaProperty for the given name or null if it doesn't exist + * + * @param name The name of the MetaProperty + * @return A MetaProperty or null + */ + MetaProperty getMetaProperty(String name); + + /** + * Retrieves a static MetaMethod for the given name and argument values, using the types of the arguments + * to establish the chosen MetaMethod + * + * @param name The name of the MetaMethod + * @param args The argument types + * @return A MetaMethod or null if it doesn't exist + */ + MetaMethod getStaticMetaMethod(String name, Object[] args); + + + /** + * Retrieves an instance MetaMethod for the given name and argument values, using the types of the + * argument values to establish the chosen MetaMethod + * + * @param name The name of the MetaMethod + * @param args Array containing - 1) the argument values (using which their types are then inferred), or 2) the corresponding argument types + * @return A MetaMethod or null if it doesn't exist + */ + MetaMethod getMetaMethod(String name, Object[] args); + + /** + * Retrieves that Java Class that the attached Meta behaviours apply to + * + * @return The java.lang.Class instance + */ + Class getTheClass(); + + /** + * Invokes a constructor for the given arguments. The MetaClass will attempt to pick the best argument which + * matches the types of the objects passed within the arguments array + * + * @param arguments The arguments to the constructor + * @return An instance of the java.lang.Class that this MetaObjectProtocol object applies to + */ + Object invokeConstructor(Object[] arguments); + + /** + * Invokes a method on the given Object with the given name and arguments. The MetaClass will attempt to pick + * the best method for the given name and arguments. If a method cannot be invoked a MissingMethodException will be + * thrown. + * + * @see groovy.lang.MissingMethodException + * @param object The instance which the method is invoked on + * @param methodName The name of the method + * @param arguments The arguments to the method + * @return The return value of the method which is null if the return type is void + */ + Object invokeMethod(Object object, String methodName, Object[] arguments); + + /** + * Invokes a method on the given object, with the given name and single argument. + * + * @see #invokeMethod(Object, String, Object[]) + * @param object The Object to invoke the method on + * @param methodName The name of the method + * @param arguments The argument to the method + * @return The return value of the method which is null if the return type is void + */ + Object invokeMethod(Object object, String methodName, Object arguments); + + /** + * Invokes a static method on the given Object with the given name and arguments. + * <p> + * The Object can either be an instance of the class that this + * MetaObjectProtocol instance applies to or the java.lang.Class instance itself. If a method cannot be invoked + * a MissingMethodException is will be thrown + * + * @see groovy.lang.MissingMethodException + * @param object An instance of the class returned by the getTheClass() method or the class itself + * @param methodName The name of the method + * @param arguments The arguments to the method + * @return The return value of the method which is null if the return type is void + */ + Object invokeStaticMethod(Object object, String methodName, Object[] arguments); + + /** + * Retrieves a property of an instance of the class returned by the getTheClass() method. + * <p> + * What this means is largely down to the MetaClass implementation, however the default case would result + * in an attempt to invoke a JavaBean getter, or if no such getter exists a public field of the instance. + * + * @see MetaClassImpl + * @param object An instance of the class returned by the getTheClass() method + * @param property The name of the property to retrieve the value for + * @return The properties value + */ + Object getProperty(Object object, String property); + + /** + * Sets a property of an instance of the class returned by the getTheClass() method. + * <p> + * What this means is largely down to the MetaClass implementation, however the default case would result + * in an attempt to invoke a JavaBean setter, or if no such setter exists to set a public field of the instance. + * + * @see MetaClassImpl + * @param object An instance of the class returned by the getTheClass() method + * @param property The name of the property to set + * @param newValue The new value of the property + */ + void setProperty(Object object, String property, Object newValue); + + /** + * Retrieves an attribute of an instance of the class returned by the getTheClass() method. + * <p> + * What this means is largely down to the MetaClass implementation, however the default case would result + * in attempt to read a field of the instance. + * + * @see MetaClassImpl + * @param object An instance of the class returned by the getTheClass() method + * @param attribute The name of the attribute to retrieve the value for + * @return The attribute value + */ + Object getAttribute(Object object, String attribute); + + /** + * Sets an attribute of an instance of the class returned by the getTheClass() method. + * <p> + * What this means is largely down to the MetaClass implementation, however the default case would result + * in an attempt to set a field of the instance. + * + * @see MetaClassImpl + * @param object An instance of the class returned by the getTheClass() method + * @param attribute The name of the attribute to set + * @param newValue The new value of the attribute + */ + void setAttribute(Object object, String attribute, Object newValue); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaProperty.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MetaProperty.java b/src/main/groovy/groovy/lang/MetaProperty.java new file mode 100644 index 0000000..d3c9633 --- /dev/null +++ b/src/main/groovy/groovy/lang/MetaProperty.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.runtime.MetaClassHelper; + +import java.lang.reflect.Modifier; + +/** + * Represents a property on a bean which may have a getter and/or a setter + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public abstract class MetaProperty { + + protected final String name; + protected Class type; + public static final String PROPERTY_SET_PREFIX = "set"; + + /** + * Constructor that sets the property name and type (class) + */ + public MetaProperty(String name, Class type) { + this.name = name; + this.type = type; + } + + /** + * @return the property of the given object + * @throws Exception if the property could not be evaluated + */ + public abstract Object getProperty(Object object); + + /** + * Sets the property on the given object to the new value + * + * @param object on which to set the property + * @param newValue the new value of the property + * @throws RuntimeException if the property could not be set + */ + public abstract void setProperty(Object object, Object newValue); + + /** + * Return the name of the property + * + * @return the name of the property + */ + public String getName() { + return name; + } + + /** + * @return the type of the property + */ + public Class getType() { + return type; + } + + /** + * Returns the access modifier. + * @return Modifier.PUBLIC + */ + public int getModifiers() { + return Modifier.PUBLIC; + } + + /** + * Gets the name for the getter for this property + * + * @return The name of the property. The name is "get"+ the capitalized propertyName + * or, in the case of boolean values, "is" + the capitalized propertyName + */ + public static String getGetterName(String propertyName, Class type) { + String prefix = type == boolean.class || type == Boolean.class ? "is" : "get"; + return prefix + MetaClassHelper.capitalize(propertyName); + } + + /** + * Gets the setter for the getter for this property. + * + * @return The name of the property. The name is "set"+ the capitalized propertyName. + */ + public static String getSetterName(String propertyName) { + return PROPERTY_SET_PREFIX + MetaClassHelper.capitalize(propertyName); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MissingClassException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MissingClassException.java b/src/main/groovy/groovy/lang/MissingClassException.java new file mode 100644 index 0000000..ad8e5f5 --- /dev/null +++ b/src/main/groovy/groovy/lang/MissingClassException.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassNode; + +/** + * An exception occurred if a dynamic method dispatch fails with an unknown class. + * + * Note that the Missing*Exception classes were named for consistency and + * to avoid conflicts with JDK exceptions of the same name. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class MissingClassException extends GroovyRuntimeException { + + private final String type; + + public MissingClassException(String type, ASTNode node, String message) { + super("No such class: " + type + " " + message, node); + this.type = type; + } + + public MissingClassException(ClassNode type, String message){ + super("No such class: " + type.getName() + " " + message); + this.type = type.getName(); + } + + /** + * + * @return The type that could not be resolved + */ + public String getType() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MissingFieldException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MissingFieldException.java b/src/main/groovy/groovy/lang/MissingFieldException.java new file mode 100644 index 0000000..3c17d39 --- /dev/null +++ b/src/main/groovy/groovy/lang/MissingFieldException.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + + +/** + * An exception occurred if a dynamic field dispatch fails with an unknown field. + * + * Note that the Missing*Exception classes were named for consistency and + * to avoid conflicts with JDK exceptions of the same name. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class MissingFieldException extends GroovyRuntimeException { + + private final String field; + private final Class type; + + public MissingFieldException(String field, Class type) { + super("No such field: " + field + " for class: " + type.getName()); + this.field = field; + this.type = type; + } + + public MissingFieldException(String field, Class type, Throwable e) { + super("No such field: " + field + " for class: " + type.getName() + ". Reason: " + e, e); + this.field = field; + this.type = type; + } + + public MissingFieldException(String message, String field, Class type) { + super(message); + this.field = field; + this.type = type; + } + + /** + * @return the name of the field that could not be found + */ + public String getField() { + return field; + } + + /** + * + * @return The type on which the field was attempted to be called + */ + public Class getType() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MissingMethodException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MissingMethodException.java b/src/main/groovy/groovy/lang/MissingMethodException.java new file mode 100644 index 0000000..0902150 --- /dev/null +++ b/src/main/groovy/groovy/lang/MissingMethodException.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.runtime.InvokerHelper; +import org.codehaus.groovy.runtime.MethodRankHelper; + +/** + * An exception occurred if a dynamic method dispatch fails with an unknown method. + * <p> + * Note that the Missing*Exception classes were named for consistency and + * to avoid conflicts with JDK exceptions of the same name. + */ +public class MissingMethodException extends GroovyRuntimeException { + + private final String method; + private final Class type; + private final boolean isStatic; + + public Object[] getArguments() { + return arguments; + } + + private final Object arguments[]; + + public MissingMethodException(String method, Class type, Object[] arguments) { + this(method, type, arguments, false); + } + + public MissingMethodException(String method, Class type, Object[] arguments, boolean isStatic) { + super(); + this.method = method; + this.type = type; + this.isStatic = isStatic; + this.arguments = arguments; + } + + public String getMessage() { + return "No signature of method: " + + (isStatic ? "static " : "") + + type.getName() + + "." + + method + + "() is applicable for argument types: (" + + InvokerHelper.toTypeString(arguments, 60) + + ") values: " + + InvokerHelper.toArrayString(arguments, 60, true) + + MethodRankHelper.getMethodSuggestionString(method, type, arguments); + } + + /** + * @return the name of the method that could not be found + */ + public String getMethod() { + return method; + } + + /** + * @return The type on which the method was attempted to be called + */ + public Class getType() { + return type; + } + + /** + * @return Whether the method was called in a static way, + * i.e. on a class rather than an object. + */ + public boolean isStatic() { + return isStatic; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MissingPropertyException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MissingPropertyException.java b/src/main/groovy/groovy/lang/MissingPropertyException.java new file mode 100644 index 0000000..270f039 --- /dev/null +++ b/src/main/groovy/groovy/lang/MissingPropertyException.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.runtime.MethodRankHelper; + + +/** + * An exception occurred if a dynamic property dispatch fails with an unknown property. + * + * Note that the Missing*Exception classes were named for consistency and + * to avoid conflicts with JDK exceptions of the same name. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class MissingPropertyException extends GroovyRuntimeException { + + public static final Object MPE = new Object(); + + private final String property; + private final Class type; + + public MissingPropertyException(String property, Class type) { + this.property = property; + this.type = type; + } + + public MissingPropertyException(String property, Class type, Throwable t) { + super(t); + this.property = property; + this.type = type; + } + + public MissingPropertyException(String message) { + super(message); + this.property = null; + this.type = null; + } + + public MissingPropertyException(String message, String property, Class type) { + super(message); + this.property = property; + this.type = type; + } + + public String getMessageWithoutLocationText() { + final Throwable cause = getCause(); + if (cause == null) { + if (super.getMessageWithoutLocationText() != null) { + return super.getMessageWithoutLocationText(); + } + return "No such property: " + property + " for class: " + type.getName() + + MethodRankHelper.getPropertySuggestionString(property, type); + } + return "No such property: " + property + " for class: " + type.getName() + ". Reason: " + cause; + } + + /** + * @return the name of the property that could not be found + */ + public String getProperty() { + return property; + } + + /** + * + * @return The type on which the property was attempted to be called + */ + public Class getType() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Mixin.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Mixin.java b/src/main/groovy/groovy/lang/Mixin.java new file mode 100644 index 0000000..22ef4b9 --- /dev/null +++ b/src/main/groovy/groovy/lang/Mixin.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Provides a mechanism for mixing in methods into a class. + * + * @deprecated static mixins have been deprecated in favour of traits (trait keyword). + * + * @author Alex Tkachman + */ [email protected] +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@GroovyASTTransformationClass("org.codehaus.groovy.ast.MixinASTTransformation") +@Deprecated +public @interface Mixin { + Class [] value (); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MutableMetaClass.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/MutableMetaClass.java b/src/main/groovy/groovy/lang/MutableMetaClass.java new file mode 100644 index 0000000..903c895 --- /dev/null +++ b/src/main/groovy/groovy/lang/MutableMetaClass.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import java.lang.reflect.Method; + +/** + * An interface that defines methods that implementers of mutable Meta classes should specify. It provides operations to perform mutations + * on the MetaClass instance. + * <p> + * Whether a MetaClass allows mutation is up to the MetaClass itself and considerations of Thread safety + * need to be taken into account when making a MetaClass mutable + * <p> + * The default implementation allows mutation of MetaClass instances before initialisation (before the initialize() method is called) + * but not after, thus ensuring Thread safety once a MetaClass has been constructed and placed in the registry + * + * @see MetaClassImpl + * @see MetaClass + * @author Graeme Rocher + * @since 1.5 + */ +public interface MutableMetaClass extends MetaClass { + + /** + * Return whether the MetaClass has been modified or not + * @return True if it has + */ + boolean isModified(); + + /** + * adds a new instance method to this MetaClass. Instance + * methods are able to overwrite the original methods of the + * class. Calling this method should not be done after + * initialise was called. + * + * @param method the method to be added + */ + void addNewInstanceMethod(Method method); + + /** + * adds a new static method to this MetaClass. This is only + * possible as long as initialise was not called. + * + * @param method the method to be added + */ + void addNewStaticMethod(Method method); + + /** + * Adds a new MetaMethod to the MetaClass + * + * @param metaMethod The MetaMethod to add + */ + void addMetaMethod(MetaMethod metaMethod); + + /** + * Adds a new MetaBeanProperty to the MetaClass + * + * @param metaBeanProperty The MetaBeanProperty instance + */ + void addMetaBeanProperty(MetaBeanProperty metaBeanProperty); + + // TODO: Add methods like addMetaConstructor, addMetaAttribute, addMetaAnnotation etc. +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Newify.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Newify.java b/src/main/groovy/groovy/lang/Newify.java new file mode 100644 index 0000000..525cecb --- /dev/null +++ b/src/main/groovy/groovy/lang/Newify.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import org.codehaus.groovy.transform.GroovyASTTransformationClass; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation that supports writing constructor call expressions without the 'new' + * keyword. Instead they can be written "Ruby-style" as a method call to a 'new' + * method or "Python-style" by just omitting the 'new' keyword. + * <p> + * It allows you to write code snippets like this ("Python-style"): + * <pre> + * {@code @Newify([Tree,Leaf])} class MyTreeProcessor { + * def myTree = Tree(Tree(Leaf("A"), Leaf("B")), Leaf("C")) + * def process() { ... } + * } + * </pre> + * or this ("Ruby-style"): + * <pre> + * {@code @Newify} class MyTreeProcessor { + * def myTree = Tree.new(Tree.new(Leaf.new("A"), Leaf.new("B")), Leaf.new("C")) + * def process() { ... } + * } + * </pre> + * + * After the AST transformation, the following code is passed on for further compilation: + * <pre> + * class MyTreeProcessor { + * def myTree = new Tree(new Tree(new Leaf("A"), new Leaf("B")), new Leaf("C")) + * def process() { ... } + * } + * </pre> + * The annotation can be used on a whole class as shown above or selectively on a particular + * method, constructor or field. + * <p> + * The "Ruby-style" new conversions occur automatically unless the 'auto=false' + * flag is given when using the annotation. You might do this if you create a new method + * using meta programming. + * <p> + * The "Python-style" conversions require you to specify each class on which you want them + * to apply. The transformation then works by matching the basename of the provided classes to any + * similarly named instance method calls not specifically bound to an object, i.e. associated + * with the 'this' object. In other words <code>Leaf("A")</code> would be transformed to + * <code>new Leaf("A")</code> but <code>x.Leaf("A")</code> would not be touched. + * <p> + * An example showing how to use the annotation at different levels: + * <pre> + * {@code @Newify(auto=false, value=Foo)} + * class Main { + * {@code @Newify} // turn auto on for field + * def field1 = java.math.BigInteger.new(42) + * def field2, field3, field4 + * + * {@code @Newify(Bar)} + * def process() { + * field2 = Bar("my bar") + * } + * + * {@code @Newify(Baz)} + * Main() { + * field3 = Foo("my foo") + * field4 = Baz("my baz") + * } + * } + * </pre> + * + * The annotation is intended to be used sparingly; perhaps in DSL scenarios or when + * using deeply nested structural types. In particular, there is no support for using + * the facility with two similarly named classes from different packages at the same time. + * Though it is OK to have different packages in different contexts. Also, there is + * no support for turning "Ruby-style" conversions off at the method, constructor or + * field level if already turned on at the class level. + * + * @author Paul King + */ [email protected] +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.TYPE, ElementType.FIELD, ElementType.LOCAL_VARIABLE}) +@GroovyASTTransformationClass("org.codehaus.groovy.transform.NewifyASTTransformation") +public @interface Newify { + Class<?>[] value() default {}; + + /** + * @return if automatic conversion of "Ruby-style" new method calls should occur + */ + boolean auto() default true; +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/NonEmptySequence.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/NonEmptySequence.java b/src/main/groovy/groovy/lang/NonEmptySequence.java new file mode 100644 index 0000000..f069b7a --- /dev/null +++ b/src/main/groovy/groovy/lang/NonEmptySequence.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 groovy.lang; + +import java.util.List; + +/** + * Represents a sequence of objects which represents one or many instances of + * of objects of a given type. The type can be omitted in which case any type of + * object can be added. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class NonEmptySequence extends Sequence { + + public NonEmptySequence() { + super(null); + } + + public NonEmptySequence(Class type) { + super(type); + } + + public NonEmptySequence(Class type, List content) { + super(type, content); + } + + public int minimumSize() { + return 1; + } +} \ No newline at end of file
