You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 00:56:31 UTC
[34/49] groovy git commit: Move source files to proper packages
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/NumberRange.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/NumberRange.java b/src/main/groovy/groovy/lang/NumberRange.java
new file mode 100644
index 0000000..52ef856
--- /dev/null
+++ b/src/main/groovy/groovy/lang/NumberRange.java
@@ -0,0 +1,629 @@
+/*
+ * 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.IteratorClosureAdapter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareEqual;
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareGreaterThan;
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareGreaterThanEqual;
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThan;
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareLessThanEqual;
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareNotEqual;
+import static org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareTo;
+import static org.codehaus.groovy.runtime.dgmimpl.NumberNumberMinus.minus;
+import static org.codehaus.groovy.runtime.dgmimpl.NumberNumberMultiply.multiply;
+import static org.codehaus.groovy.runtime.dgmimpl.NumberNumberPlus.plus;
+
+/**
+ * Represents an immutable list of Numbers from a value to a value with a particular step size.
+ *
+ * In general, it isn't recommended using a NumberRange as a key to a map. The range
+ * 0..3 is deemed to be equal to 0.0..3.0 but they have different hashCode values,
+ * so storing a value using one of these ranges couldn't be retrieved using the other.
+ *
+ * @since 2.5.0
+ */
+public class NumberRange extends AbstractList<Comparable> implements Range<Comparable> {
+
+ /**
+ * The first value in the range.
+ */
+ private final Comparable from;
+
+ /**
+ * The last value in the range.
+ */
+ private final Comparable to;
+
+ /**
+ * The step size in the range.
+ */
+ private final Number stepSize;
+
+ /**
+ * The cached size, or -1 if not yet computed
+ */
+ private int size = -1;
+
+ /**
+ * The cached hashCode (once calculated)
+ */
+ private Integer hashCodeCache = null;
+
+ /**
+ * <code>true</code> if the range counts backwards from <code>to</code> to <code>from</code>.
+ */
+ private final boolean reverse;
+
+ /**
+ * <code>true</code> if the range includes the upper bound.
+ */
+ private final boolean inclusive;
+
+ /**
+ * Creates an inclusive {@link NumberRange} with step size 1.
+ * Creates a reversed range if <code>from</code> < <code>to</code>.
+ *
+ * @param from the first value in the range
+ * @param to the last value in the range
+ */
+ public <T extends Number & Comparable, U extends Number & Comparable>
+ NumberRange(T from, U to) {
+ this(from, to, null, true);
+ }
+
+ /**
+ * Creates a new {@link NumberRange} with step size 1.
+ * Creates a reversed range if <code>from</code> < <code>to</code>.
+ *
+ * @param from start of the range
+ * @param to end of the range
+ * @param inclusive whether the range is inclusive
+ */
+ public <T extends Number & Comparable, U extends Number & Comparable>
+ NumberRange(T from, U to, boolean inclusive) {
+ this(from, to, null, inclusive);
+ }
+
+ /**
+ * Creates an inclusive {@link NumberRange}.
+ * Creates a reversed range if <code>from</code> < <code>to</code>.
+ *
+ * @param from start of the range
+ * @param to end of the range
+ * @param stepSize the gap between discrete elements in the range
+ */
+ public <T extends Number & Comparable, U extends Number & Comparable, V extends
+ Number & Comparable<? super Number>>
+ NumberRange(T from, U to, V stepSize) {
+ this(from, to, stepSize, true);
+ }
+
+ /**
+ * Creates a {@link NumberRange}.
+ * Creates a reversed range if <code>from</code> < <code>to</code>.
+ *
+ * @param from start of the range
+ * @param to end of the range
+ * @param stepSize the gap between discrete elements in the range
+ * @param inclusive whether the range is inclusive
+ */
+ public <T extends Number & Comparable, U extends Number & Comparable, V extends
+ Number & Comparable>
+ NumberRange(T from, U to, V stepSize, boolean inclusive) {
+ if (from == null) {
+ throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
+ }
+ if (to == null) {
+ throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
+ }
+ reverse = areReversed(from, to);
+ Number tempFrom;
+ Number tempTo;
+ if (reverse) {
+ tempFrom = to;
+ tempTo = from;
+ } else {
+ tempFrom = from;
+ tempTo = to;
+ }
+ if (tempFrom instanceof Short) {
+ tempFrom = tempFrom.intValue();
+ } else if (tempFrom instanceof Float) {
+ tempFrom = tempFrom.doubleValue();
+ }
+ if (tempTo instanceof Short) {
+ tempTo = tempTo.intValue();
+ } else if (tempTo instanceof Float) {
+ tempTo = tempTo.doubleValue();
+ }
+
+ if (tempFrom instanceof Integer && tempTo instanceof Long) {
+ tempFrom = tempFrom.longValue();
+ } else if (tempTo instanceof Integer && tempFrom instanceof Long) {
+ tempTo = tempTo.longValue();
+ }
+
+ this.from = (Comparable) tempFrom;
+ this.to = (Comparable) tempTo;
+ this.stepSize = stepSize == null ? 1 : stepSize;
+ this.inclusive = inclusive;
+ }
+
+ /**
+ * For a NumberRange with step size 1, creates a new NumberRange with the same
+ * <code>from</code> and <code>to</code> as this NumberRange
+ * but with a step size of <code>stepSize</code>.
+ *
+ * @param stepSize the desired step size
+ * @return a new NumberRange
+ */
+ public <T extends Number & Comparable> NumberRange by(T stepSize) {
+ if (!Integer.valueOf(1).equals(this.stepSize)) {
+ throw new IllegalStateException("by only allowed on ranges with original stepSize = 1 but found " + this.stepSize);
+ }
+ return new NumberRange(comparableNumber(from), comparableNumber(to), stepSize, inclusive);
+ }
+
+ @SuppressWarnings("unchecked")
+ /* package private */ static <T extends Number & Comparable> T comparableNumber(Comparable c) {
+ return (T) c;
+ }
+
+ @SuppressWarnings("unchecked")
+ /* package private */ static <T extends Number & Comparable> T comparableNumber(Number n) {
+ return (T) n;
+ }
+
+ private static boolean areReversed(Number from, Number to) {
+ try {
+ return compareGreaterThan(from, to);
+ } catch (ClassCastException cce) {
+ throw new IllegalArgumentException("Unable to create range due to incompatible types: " + from.getClass().getSimpleName() + ".." + to.getClass().getSimpleName() + " (possible missing brackets around range?)", cce);
+ }
+ }
+
+ /**
+ * An object is deemed equal to this NumberRange if it represents a List of items and
+ * those items equal the list of discrete items represented by this NumberRange.
+ *
+ * @param that the object to be compared for equality with this NumberRange
+ * @return {@code true} if the specified object is equal to this NumberRange
+ * @see #fastEquals(NumberRange)
+ */
+ @Override
+ public boolean equals(Object that) {
+ return super.equals(that);
+ }
+
+ /**
+ * A NumberRange's hashCode is based on hashCode values of the discrete items it represents.
+ *
+ * @return the hashCode value
+ */
+ @Override
+ public int hashCode() {
+ if (hashCodeCache == null) {
+ hashCodeCache = super.hashCode();
+ }
+ return hashCodeCache;
+ }
+
+ /*
+ * NOTE: as per the class javadoc, this class doesn't obey the normal equals/hashCode contract.
+ * The following field and method could assist some scenarios which required a similar sort of contract
+ * (but between equals and the custom canonicalHashCode). Currently commented out since we haven't
+ * found a real need. We will likely remove this commented out code if no usage is identified soon.
+ */
+
+ /*
+ * The cached canonical hashCode (once calculated)
+ */
+// private Integer canonicalHashCodeCache = null;
+
+ /*
+ * A NumberRange's canonicalHashCode is based on hashCode values of the discrete items it represents.
+ * When two NumberRange's are equal they will have the same canonicalHashCode value.
+ * Numerical values which Groovy deems equal have the same hashCode during this calculation.
+ * So currently (0..3).equals(0.0..3.0) yet they have different hashCode values. This breaks
+ * the normal equals/hashCode contract which is a weakness in Groovy's '==' operator. However
+ * the contract isn't broken between equals and canonicalHashCode.
+ *
+ * @return the hashCode value
+ */
+// public int canonicalHashCode() {
+// if (canonicalHashCodeCache == null) {
+// int hashCode = 1;
+// for (Comparable e : this) {
+// int value;
+// if (e == null) {
+// value = 0;
+// } else {
+// BigDecimal next = new BigDecimal(e.toString());
+// if (next.compareTo(BigDecimal.ZERO) == 0) {
+// // workaround on pre-Java8 for http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6480539
+// value = BigDecimal.ZERO.hashCode();
+// } else {
+// value = next.stripTrailingZeros().hashCode();
+// }
+// }
+// hashCode = 31 * hashCode + value;
+// }
+// canonicalHashCodeCache = hashCode;
+// }
+// return canonicalHashCodeCache;
+// }
+
+ /**
+ * Compares a {@link NumberRange} to another {@link NumberRange} using only a strict comparison
+ * of the NumberRange properties. This won't return true for some ranges which represent the same
+ * discrete items, use equals instead for that but will be much faster for large lists.
+ *
+ * @param that the NumberRange to check equality with
+ * @return <code>true</code> if the ranges are equal
+ */
+ public boolean fastEquals(NumberRange that) {
+ return that != null
+ && reverse == that.reverse
+ && inclusive == that.inclusive
+ && compareEqual(from, that.from)
+ && compareEqual(to, that.to)
+ && compareEqual(stepSize, that.stepSize);
+ }
+
+ /*
+ * NOTE: as per the class javadoc, this class doesn't obey the normal equals/hashCode contract.
+ * The following field and method could assist some scenarios which required a similar sort of contract
+ * (but between fastEquals and the custom fastHashCode). Currently commented out since we haven't
+ * found a real need. We will likely remove this commented out code if no usage is identified soon.
+ */
+
+ /*
+ * The cached fast hashCode (once calculated)
+ */
+// private Integer fastHashCodeCache = null;
+
+ /*
+ * A hashCode function that pairs with fastEquals, following the normal equals/hashCode contract.
+ *
+ * @return the calculated hash code
+ */
+// public int fastHashCode() {
+// if (fastHashCodeCache == null) {
+// int result = 17;
+// result = result * 31 + (reverse ? 1 : 0);
+// result = result * 31 + (inclusive ? 1 : 0);
+// result = result * 31 + new BigDecimal(from.toString()).stripTrailingZeros().hashCode();
+// result = result * 31 + new BigDecimal(to.toString()).stripTrailingZeros().hashCode();
+// result = result * 31 + new BigDecimal(stepSize.toString()).stripTrailingZeros().hashCode();
+// fastHashCodeCache = result;
+// }
+// return fastHashCodeCache;
+// }
+
+ @Override
+ public Comparable getFrom() {
+ return from;
+ }
+
+ @Override
+ public Comparable getTo() {
+ return to;
+ }
+
+ public Comparable getStepSize() {
+ return (Comparable) stepSize;
+ }
+
+ @Override
+ public boolean isReverse() {
+ return reverse;
+ }
+
+ @Override
+ public Comparable get(int index) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
+ }
+ final Iterator<Comparable> iter = new StepIterator(this, stepSize);
+
+ Comparable value = iter.next();
+ for (int i = 0; i < index; i++) {
+ if (!iter.hasNext()) {
+ throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
+ }
+ value = iter.next();
+ }
+ return value;
+ }
+
+ /**
+ * Checks whether a value is between the from and to values of a Range
+ *
+ * @param value the value of interest
+ * @return true if the value is within the bounds
+ */
+ @Override
+ public boolean containsWithinBounds(Object value) {
+ final int result = compareTo(from, value);
+ return result == 0 || result < 0 && compareTo(to, value) >= 0;
+ }
+
+ /**
+ * protection against calls from Groovy
+ */
+ @SuppressWarnings("unused")
+ private void setSize(int size) {
+ throw new UnsupportedOperationException("size must not be changed");
+ }
+
+ @Override
+ public int size() {
+ if (size == -1) {
+ calcSize(from, to, stepSize);
+ }
+ return size;
+ }
+
+ void calcSize(Comparable from, Comparable to, Number stepSize) {
+ int tempsize = 0;
+ boolean shortcut = false;
+ if (isIntegral(stepSize)) {
+ if ((from instanceof Integer || from instanceof Long)
+ && (to instanceof Integer || to instanceof Long)) {
+ // let's fast calculate the size
+ final BigInteger fromNum = new BigInteger(from.toString());
+ final BigInteger toTemp = new BigInteger(to.toString());
+ final BigInteger toNum = inclusive ? toTemp : toTemp.subtract(BigInteger.ONE);
+ final BigInteger sizeNum = new BigDecimal(toNum.subtract(fromNum)).divide(new BigDecimal(stepSize.longValue()), BigDecimal.ROUND_DOWN).toBigInteger().add(BigInteger.ONE);
+ tempsize = sizeNum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == -1 ? sizeNum.intValue() : Integer.MAX_VALUE;
+ shortcut = true;
+ } else if (((from instanceof BigDecimal || from instanceof BigInteger) && to instanceof Number) ||
+ ((to instanceof BigDecimal || to instanceof BigInteger) && from instanceof Number)) {
+ // let's fast calculate the size
+ final BigDecimal fromNum = new BigDecimal(from.toString());
+ final BigDecimal toTemp = new BigDecimal(to.toString());
+ final BigDecimal toNum = inclusive ? toTemp : toTemp.subtract(new BigDecimal("1.0"));
+ final BigInteger sizeNum = toNum.subtract(fromNum).divide(new BigDecimal(stepSize.longValue()), BigDecimal.ROUND_DOWN).toBigInteger().add(BigInteger.ONE);
+ tempsize = sizeNum.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == -1 ? sizeNum.intValue() : Integer.MAX_VALUE;
+ shortcut = true;
+ }
+ }
+ if (!shortcut) {
+ // let's brute-force calculate the size by iterating start to end
+ final Iterator iter = new StepIterator(this, stepSize);
+ while (iter.hasNext()) {
+ tempsize++;
+ // integer overflow
+ if (tempsize < 0) {
+ break;
+ }
+ iter.next();
+ }
+ // integer overflow
+ if (tempsize < 0) {
+ tempsize = Integer.MAX_VALUE;
+ }
+ }
+ size = tempsize;
+ }
+
+ private boolean isIntegral(Number stepSize) {
+ BigDecimal tempStepSize = new BigDecimal(stepSize.toString());
+ return tempStepSize.equals(new BigDecimal(tempStepSize.toBigInteger()));
+ }
+
+ @Override
+ public List<Comparable> subList(int fromIndex, int toIndex) {
+ if (fromIndex < 0) {
+ throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+ }
+ if (fromIndex > toIndex) {
+ throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+ }
+ if (fromIndex == toIndex) {
+ return new EmptyRange<Comparable>(from);
+ }
+
+ // Performance detail:
+ // not using get(fromIndex), get(toIndex) in the following to avoid stepping over elements twice
+ final Iterator<Comparable> iter = new StepIterator(this, stepSize);
+
+ Comparable value = iter.next();
+ int i = 0;
+ for (; i < fromIndex; i++) {
+ if (!iter.hasNext()) {
+ throw new IndexOutOfBoundsException("Index: " + i + " is too big for range: " + this);
+ }
+ value = iter.next();
+ }
+ final Comparable fromValue = value;
+ for (; i < toIndex - 1; i++) {
+ if (!iter.hasNext()) {
+ throw new IndexOutOfBoundsException("Index: " + i + " is too big for range: " + this);
+ }
+ value = iter.next();
+ }
+ final Comparable toValue = value;
+
+ return new NumberRange(comparableNumber(fromValue), comparableNumber(toValue), comparableNumber(stepSize), true);
+ }
+
+ @Override
+ public String toString() {
+ return getToString(to.toString(), from.toString());
+ }
+
+ @Override
+ public String inspect() {
+ return getToString(InvokerHelper.inspect(to), InvokerHelper.inspect(from));
+ }
+
+ private String getToString(String toText, String fromText) {
+ String sep = inclusive ? ".." : "..<";
+ String base = reverse ? "" + toText + sep + fromText : "" + fromText + sep + toText;
+ return Integer.valueOf(1).equals(stepSize) ? base : base + ".by(" + stepSize + ")";
+ }
+
+ /**
+ * iterates over all values and returns true if one value matches.
+ * Also see containsWithinBounds.
+ */
+ @Override
+ public boolean contains(Object value) {
+ if (value == null) {
+ return false;
+ }
+ final Iterator it = new StepIterator(this, stepSize);
+ while (it.hasNext()) {
+ if (compareEqual(value, it.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void step(int numSteps, Closure closure) {
+ if (numSteps == 0 && compareTo(from, to) == 0) {
+ return; // from == to and step == 0, nothing to do, so return
+ }
+ final StepIterator iter = new StepIterator(this, multiply(numSteps, stepSize));
+ while (iter.hasNext()) {
+ closure.call(iter.next());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator<Comparable> iterator() {
+ return new StepIterator(this, stepSize);
+ }
+
+ /**
+ * convenience class to serve in other methods.
+ * It's not thread-safe, and lazily produces the next element only on calls of hasNext() or next()
+ */
+ private class StepIterator implements Iterator<Comparable> {
+ private final NumberRange range;
+ private final Number step;
+ private final boolean isAscending;
+
+ private boolean isNextFetched = false;
+ private Comparable next = null;
+
+ StepIterator(NumberRange range, Number step) {
+ if (compareEqual(step, 0) && compareNotEqual(range.getFrom(), range.getTo())) {
+ throw new GroovyRuntimeException("Infinite loop detected due to step size of 0");
+ }
+
+ this.range = range;
+ if (compareLessThan(step, 0)) {
+ this.step = multiply(step, -1);
+ isAscending = range.isReverse();
+ } else {
+ this.step = step;
+ isAscending = !range.isReverse();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ fetchNextIfNeeded();
+ return (next != null) && (isAscending
+ ? (range.inclusive ? compareLessThanEqual(next, range.getTo()) : compareLessThan(next, range.getTo()))
+ : (range.inclusive ? compareGreaterThanEqual(next, range.getFrom()) : compareGreaterThan(next, range.getFrom())));
+ }
+
+ @Override
+ public Comparable next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ fetchNextIfNeeded();
+ isNextFetched = false;
+ return next;
+ }
+
+ private void fetchNextIfNeeded() {
+ if (!isNextFetched) {
+ isNextFetched = true;
+
+ if (next == null) {
+ // make the first fetch lazy too
+ next = isAscending ? range.getFrom() : range.getTo();
+ } else {
+ next = isAscending ? increment(next, step) : decrement(next, step);
+ }
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Override
+ public List<Comparable> step(int numSteps) {
+ final IteratorClosureAdapter<Comparable> adapter = new IteratorClosureAdapter<Comparable>(this);
+ step(numSteps, adapter);
+ return adapter.asList();
+ }
+
+ /**
+ * Increments by given step
+ *
+ * @param value the value to increment
+ * @param step the amount to increment
+ * @return the incremented value
+ */
+ @SuppressWarnings("unchecked")
+ private Comparable increment(Object value, Number step) {
+ return (Comparable) plus((Number) value, step);
+ }
+
+ /**
+ * Decrements by given step
+ *
+ * @param value the value to decrement
+ * @param step the amount to decrement
+ * @return the decremented value
+ */
+ @SuppressWarnings("unchecked")
+ private Comparable decrement(Object value, Number step) {
+ return (Comparable) minus((Number) value, step);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ObjectRange.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ObjectRange.java b/src/main/groovy/groovy/lang/ObjectRange.java
new file mode 100644
index 0000000..a7e2b05
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ObjectRange.java
@@ -0,0 +1,539 @@
+/*
+ * 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.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.IteratorClosureAdapter;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Represents an inclusive list of objects from a value to a value using
+ * comparators.
+ * <p>
+ * Note: This class is similar to {@link IntRange}. If you make any changes to this
+ * class, you might consider making parallel changes to {@link IntRange}.
+ */
+public class ObjectRange extends AbstractList<Comparable> implements Range<Comparable> {
+ /**
+ * The first value in the range.
+ */
+ private final Comparable from;
+
+ /**
+ * The last value in the range.
+ */
+ private final Comparable to;
+
+ /**
+ * The cached size, or -1 if not yet computed
+ */
+ private int size = -1;
+
+ /**
+ * <code>true</code> if the range counts backwards from <code>to</code> to <code>from</code>.
+ */
+ private final boolean reverse;
+
+ /**
+ * Creates a new {@link ObjectRange}. Creates a reversed range if
+ * <code>from</code> < <code>to</code>.
+ *
+ * @param from the first value in the range.
+ * @param to the last value in the range.
+ */
+ public ObjectRange(Comparable from, Comparable to) {
+ this(from, to, null);
+ }
+
+ /**
+ * Creates a new {@link ObjectRange} assumes smaller <= larger, else behavior is undefined.
+ * Caution: Prefer the other constructor when in doubt.
+ * <p>
+ * Optimized Constructor avoiding initial computation of comparison.
+ */
+ public ObjectRange(Comparable smaller, Comparable larger, boolean reverse) {
+ this(smaller, larger, (Boolean) reverse);
+ }
+
+ /**
+ * Constructs a Range, computing reverse if not provided. When providing reverse,
+ * 'smaller' must not be larger than 'larger'.
+ *
+ * @param smaller start of the range, must no be larger than to when reverse != null
+ * @param larger end of the range, must be larger than from when reverse != null
+ * @param reverse direction of the range. If null, causes direction to be computed (can be expensive).
+ */
+ private ObjectRange(Comparable smaller, Comparable larger, Boolean reverse) {
+ if (smaller == null) {
+ throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
+ }
+ if (larger == null) {
+ throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
+ }
+ if (reverse == null) {
+ final boolean computedReverse = areReversed(smaller, larger);
+ // ensure invariant from <= to
+ if (computedReverse) {
+ final Comparable temp = larger;
+ larger = smaller;
+ smaller = temp;
+ }
+ this.reverse = computedReverse;
+ } else {
+ this.reverse = reverse;
+ }
+
+ if (smaller instanceof Short) {
+ smaller = ((Short) smaller).intValue();
+ } else if (smaller instanceof Float) {
+ smaller = ((Float) smaller).doubleValue();
+ }
+ if (larger instanceof Short) {
+ larger = ((Short) larger).intValue();
+ } else if (larger instanceof Float) {
+ larger = ((Float) larger).doubleValue();
+ }
+
+ if (smaller instanceof Integer && larger instanceof Long) {
+ smaller = ((Integer) smaller).longValue();
+ } else if (larger instanceof Integer && smaller instanceof Long) {
+ larger = ((Integer) larger).longValue();
+ }
+
+ /*
+ areReversed() already does an implicit type compatibility check
+ based on DefaultTypeTransformation.compareToWithEqualityCheck() for mixed classes
+ but it is only invoked if reverse == null.
+ So Object Range has to perform those type checks for consistency even when not calling
+ compareToWithEqualityCheck(), and ObjectRange has
+ to use the normalized value used in a successful comparison in
+ compareToWithEqualityCheck(). Currently that means Chars and single-char Strings
+ are evaluated as the char's charValue (an integer) when compared to numbers.
+ So '7'..'9' should produce ['7', '8', '9'], whereas ['7'..9] and [7..'9'] should produce [55, 56, 57].
+ if classes match, or both numerical, no checks possible / necessary
+ */
+ if (smaller.getClass() == larger.getClass() ||
+ (smaller instanceof Number && larger instanceof Number)) {
+ this.from = smaller;
+ this.to = larger;
+ } else {
+ // Convenience hack: try convert single-char strings to ints
+ final Comparable tempfrom = normaliseStringType(smaller);
+ final Comparable tempto = normaliseStringType(larger);
+ // if after normalizing both are numbers, assume intended range was numbers
+ if (tempfrom instanceof Number && tempto instanceof Number) {
+ this.from = tempfrom;
+ this.to = tempto;
+ } else {
+ // if convenience hack did not make classes match,
+ // throw exception when starting with known class, and thus "from" cannot be advanced over "to".
+ // Note if start is an unusual Object, it could have a next() method
+ // that yields a Number or String to close the range
+ final Comparable start = this.reverse ? larger : smaller;
+ if (start instanceof String || start instanceof Number) {
+ // starting with number will never reach a non-number, same for string
+ throw new IllegalArgumentException("Incompatible Argument classes for ObjectRange " + smaller.getClass() + ", " + larger.getClass());
+ }
+ // Since normalizing did not help, use original values at user's risk
+ this.from = smaller;
+ this.to = larger;
+ }
+ }
+ checkBoundaryCompatibility();
+ }
+
+ /**
+ * throws IllegalArgumentException if to and from are incompatible, meaning they e.g. (likely) produce infinite sequences.
+ * Called at construction time, subclasses may override cautiously (using only members to and from).
+ */
+ protected void checkBoundaryCompatibility() {
+ if (from instanceof String && to instanceof String) {
+ // this test depends deeply on the String.next implementation
+ // 009.next is 00:, not 010
+ final String start = from.toString();
+ final String end = to.toString();
+ if (start.length() != end.length()) {
+ throw new IllegalArgumentException("Incompatible Strings for Range: different length");
+ }
+ final int length = start.length();
+ int i;
+ for (i = 0; i < length; i++) {
+ if (start.charAt(i) != end.charAt(i)) {
+ break;
+ }
+ }
+ // strings must be equal except for the last character
+ if (i < length - 1) {
+ throw new IllegalArgumentException("Incompatible Strings for Range: String#next() will not reach the expected value");
+ }
+ }
+ }
+
+ private static boolean areReversed(Comparable from, Comparable to) {
+ try {
+ return ScriptBytecodeAdapter.compareGreaterThan(from, to);
+ } catch (IllegalArgumentException iae) {
+ throw new IllegalArgumentException("Unable to create range due to incompatible types: " + from.getClass().getSimpleName() + ".." + to.getClass().getSimpleName() + " (possible missing brackets around range?)", iae);
+ }
+ }
+
+ public boolean equals(Object that) {
+ return (that instanceof ObjectRange) ? equals((ObjectRange) that) : super.equals(that);
+ }
+
+ /**
+ * Compares an {@link ObjectRange} to another {@link ObjectRange}.
+ *
+ * @param that the object to check equality with
+ * @return <code>true</code> if the ranges are equal
+ */
+ public boolean equals(ObjectRange that) {
+ return that != null
+ && reverse == that.reverse
+ && DefaultTypeTransformation.compareEqual(from, that.from)
+ && DefaultTypeTransformation.compareEqual(to, that.to);
+ }
+
+ @Override
+ public Comparable getFrom() {
+ return from;
+ }
+
+ @Override
+ public Comparable getTo() {
+ return to;
+ }
+
+ @Override
+ public boolean isReverse() {
+ return reverse;
+ }
+
+ @Override
+ public Comparable get(int index) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
+ }
+ final StepIterator iter = new StepIterator(this, 1);
+
+ Comparable value = iter.next();
+ for (int i = 0; i < index; i++) {
+ if (!iter.hasNext()) {
+ throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
+ }
+ value = iter.next();
+ }
+ return value;
+ }
+
+ /**
+ * Checks whether a value is between the from and to values of a Range
+ *
+ * @param value the value of interest
+ * @return true if the value is within the bounds
+ */
+ @Override
+ public boolean containsWithinBounds(Object value) {
+ if (value instanceof Comparable) {
+ final int result = compareTo(from, (Comparable) value);
+ return result == 0 || result < 0 && compareTo(to, (Comparable) value) >= 0;
+ }
+ return contains(value);
+ }
+
+ protected int compareTo(Comparable first, Comparable second) {
+ return DefaultGroovyMethods.numberAwareCompareTo(first, second);
+ }
+
+ /**
+ * protection against calls from Groovy
+ */
+ @SuppressWarnings("unused")
+ private void setSize(int size) {
+ throw new UnsupportedOperationException("size must not be changed");
+ }
+
+ @Override
+ public int size() {
+ if (size == -1) {
+ int tempsize = 0;
+ if ((from instanceof Integer || from instanceof Long)
+ && (to instanceof Integer || to instanceof Long)) {
+ // let's fast calculate the size
+ final BigInteger fromNum = new BigInteger(from.toString());
+ final BigInteger toNum = new BigInteger(to.toString());
+ final BigInteger sizeNum = toNum.subtract(fromNum).add(new BigInteger("1"));
+ tempsize = sizeNum.intValue();
+ if (!BigInteger.valueOf(tempsize).equals(sizeNum)) {
+ tempsize = Integer.MAX_VALUE;
+ }
+ } else if (from instanceof Character && to instanceof Character) {
+ // let's fast calculate the size
+ final char fromNum = (Character) from;
+ final char toNum = (Character) to;
+ tempsize = toNum - fromNum + 1;
+ } else if (((from instanceof BigDecimal || from instanceof BigInteger) && to instanceof Number) ||
+ ((to instanceof BigDecimal || to instanceof BigInteger) && from instanceof Number)) {
+ // let's fast calculate the size
+ final BigDecimal fromNum = new BigDecimal(from.toString());
+ final BigDecimal toNum = new BigDecimal(to.toString());
+ final BigInteger sizeNum = toNum.subtract(fromNum).add(new BigDecimal(1.0)).toBigInteger();
+ tempsize = sizeNum.intValue();
+ if (!BigInteger.valueOf(tempsize).equals(sizeNum)) {
+ tempsize = Integer.MAX_VALUE;
+ }
+ } else {
+ // let's brute-force calculate the size by iterating start to end
+ final Iterator<Comparable> iter = new StepIterator(this, 1);
+ while (iter.hasNext()) {
+ tempsize++;
+ // integer overflow
+ if (tempsize < 0) {
+ break;
+ }
+ iter.next();
+ }
+ }
+ // integer overflow
+ if (tempsize < 0) {
+ tempsize = Integer.MAX_VALUE;
+ }
+ size = tempsize;
+ }
+ return size;
+ }
+
+ @Override
+ public List<Comparable> subList(int fromIndex, int toIndex) {
+ if (fromIndex < 0) {
+ throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+ }
+ if (fromIndex > toIndex) {
+ throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+ }
+ if (fromIndex == toIndex) {
+ return new EmptyRange<Comparable>(from);
+ }
+
+ // Performance detail:
+ // not using get(fromIndex), get(toIndex) in the following to avoid stepping over elements twice
+ final Iterator<Comparable> iter = new StepIterator(this, 1);
+
+ Comparable toValue = iter.next();
+ int i = 0;
+ for (; i < fromIndex; i++) {
+ if (!iter.hasNext()) {
+ throw new IndexOutOfBoundsException("Index: " + i + " is too big for range: " + this);
+ }
+ toValue = iter.next();
+ }
+ final Comparable fromValue = toValue;
+ for (; i < toIndex - 1; i++) {
+ if (!iter.hasNext()) {
+ throw new IndexOutOfBoundsException("Index: " + i + " is too big for range: " + this);
+ }
+ toValue = iter.next();
+ }
+
+ return new ObjectRange(fromValue, toValue, reverse);
+ }
+
+ public String toString() {
+ return reverse ? "" + to + ".." + from : "" + from + ".." + to;
+ }
+
+ @Override
+ public String inspect() {
+ final String toText = InvokerHelper.inspect(to);
+ final String fromText = InvokerHelper.inspect(from);
+ return reverse ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
+ }
+
+ /**
+ * Iterates over all values and returns true if one value matches.
+ *
+ * @see #containsWithinBounds(Object)
+ */
+ @Override
+ public boolean contains(Object value) {
+ final Iterator<Comparable> iter = new StepIterator(this, 1);
+ if (value == null) {
+ return false;
+ }
+ while (iter.hasNext()) {
+ if (DefaultTypeTransformation.compareEqual(value, iter.next())) return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void step(int step, Closure closure) {
+ if (step == 0 && compareTo(from, to) == 0) {
+ return; // from == to and step == 0, nothing to do, so return
+ }
+ final Iterator<Comparable> iter = new StepIterator(this, step);
+ while (iter.hasNext()) {
+ closure.call(iter.next());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Iterator<Comparable> iterator() {
+ // non thread-safe iterator
+ return new StepIterator(this, 1);
+ }
+
+ /**
+ * Non-thread-safe iterator which lazily produces the next element only on calls of hasNext() or next()
+ */
+ private static final class StepIterator implements Iterator<Comparable> {
+ // actual step, can be +1 when desired step is -1 and direction is from high to low
+ private final int step;
+ private final ObjectRange range;
+ private int index = -1;
+ private Comparable value;
+ private boolean nextFetched = true;
+
+ private StepIterator(ObjectRange range, final int desiredStep) {
+ if (desiredStep == 0 && range.compareTo(range.getFrom(), range.getTo()) != 0) {
+ throw new GroovyRuntimeException("Infinite loop detected due to step size of 0");
+ }
+ this.range = range;
+ if (range.isReverse()) {
+ step = -desiredStep;
+ } else {
+ step = desiredStep;
+ }
+ if (step > 0) {
+ value = range.getFrom();
+ } else {
+ value = range.getTo();
+ }
+ }
+
+ @Override
+ public void remove() {
+ range.remove(index);
+ }
+
+ @Override
+ public Comparable next() {
+ // not thread safe
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ nextFetched = false;
+ index++;
+ return value;
+ }
+
+ @Override
+ public boolean hasNext() {
+ // not thread safe
+ if (!nextFetched) {
+ value = peek();
+ nextFetched = true;
+ }
+ return value != null;
+ }
+
+ private Comparable peek() {
+ if (step > 0) {
+ Comparable peekValue = value;
+ for (int i = 0; i < step; i++) {
+ peekValue = (Comparable) range.increment(peekValue);
+ // handle back to beginning due to modulo incrementing
+ if (range.compareTo(peekValue, range.from) <= 0) return null;
+ }
+ if (range.compareTo(peekValue, range.to) <= 0) {
+ return peekValue;
+ }
+ } else {
+ final int positiveStep = -step;
+ Comparable peekValue = value;
+ for (int i = 0; i < positiveStep; i++) {
+ peekValue = (Comparable) range.decrement(peekValue);
+ // handle back to beginning due to modulo decrementing
+ if (range.compareTo(peekValue, range.to) >= 0) return null;
+ }
+ if (range.compareTo(peekValue, range.from) >= 0) {
+ return peekValue;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public List<Comparable> step(int step) {
+ final IteratorClosureAdapter<Comparable> adapter = new IteratorClosureAdapter<Comparable>(this);
+ step(step, adapter);
+ return adapter.asList();
+ }
+
+ /**
+ * Increments by one
+ *
+ * @param value the value to increment
+ * @return the incremented value
+ */
+ protected Object increment(Object value) {
+ return InvokerHelper.invokeMethod(value, "next", null);
+ }
+
+ /**
+ * Decrements by one
+ *
+ * @param value the value to decrement
+ * @return the decremented value
+ */
+ protected Object decrement(Object value) {
+ return InvokerHelper.invokeMethod(value, "previous", null);
+ }
+
+ /**
+ * if operand is a Character or a String with one character, return that character's int value.
+ */
+ private static Comparable normaliseStringType(final Comparable operand) {
+ if (operand instanceof Character) {
+ return (int) (Character) operand;
+ }
+ if (operand instanceof String) {
+ final String string = (String) operand;
+
+ if (string.length() == 1) {
+ return (int) string.charAt(0);
+ }
+ return string;
+ }
+ return operand;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ParameterArray.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ParameterArray.java b/src/main/groovy/groovy/lang/ParameterArray.java
new file mode 100644
index 0000000..d3a4163
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ParameterArray.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+/**
+ * Distinguish a parameter array from Object[].
+ *
+ * @author Pilho Kim
+ */
+public class ParameterArray {
+
+ private final Object parameters;
+
+ public ParameterArray(Object data) {
+ parameters = packArray(data);
+ }
+
+ private static Object packArray(Object object) {
+ if (object instanceof Object[])
+ return (Object[]) object;
+ else
+ return object;
+ }
+
+ public Object get() {
+ return parameters;
+ }
+
+ public String toString() {
+ if (parameters == null)
+ return "<null parameter>";
+ return parameters.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/PropertyAccessInterceptor.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/PropertyAccessInterceptor.java b/src/main/groovy/groovy/lang/PropertyAccessInterceptor.java
new file mode 100644
index 0000000..6d2906e
--- /dev/null
+++ b/src/main/groovy/groovy/lang/PropertyAccessInterceptor.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+/**
+ * <p>An interface that adds the ability to intercept
+ * property getters/setters
+ *
+ * @author Graeme Rocher
+ * @since Oct 24, 2005
+ */
+public interface PropertyAccessInterceptor extends Interceptor {
+
+ /**
+ * Intercepts a getXXX call and returns a result. The result is replaced by the
+ * real value if doGet() return false
+ *
+ * @param object The target object
+ * @param property The property to get
+ * @return A value supplied by the interceptor
+ */
+ Object beforeGet(Object object, String property);
+
+ /**
+ * Intercepts a setXXX call
+ *
+ * @param object The target object
+ * @param property The property to set
+ * @param newValue The new value
+ */
+ void beforeSet(Object object, String property, Object newValue);
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/PropertyValue.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/PropertyValue.java b/src/main/groovy/groovy/lang/PropertyValue.java
new file mode 100644
index 0000000..ac77b54
--- /dev/null
+++ b/src/main/groovy/groovy/lang/PropertyValue.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+public class PropertyValue {
+ // the owner of the property
+ private final Object bean;
+
+ // the description of the property
+ private final MetaProperty mp;
+
+ public PropertyValue(Object bean, MetaProperty mp) {
+ this.bean = bean;
+ this.mp = mp;
+ }
+
+ public String getName() {
+ return mp.getName();
+ }
+
+ public Class getType() {
+ return mp.getType();
+ }
+
+ public Object getValue() {
+ return mp.getProperty(bean);
+ }
+
+ public void setValue(Object value) {
+ mp.setProperty(bean, value);
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ProxyMetaClass.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ProxyMetaClass.java b/src/main/groovy/groovy/lang/ProxyMetaClass.java
new file mode 100644
index 0000000..8c41f7d
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ProxyMetaClass.java
@@ -0,0 +1,242 @@
+/*
+ * 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;
+
+/**
+ * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
+ * It enriches MetaClass with the feature of making method invocations interceptable by
+ * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
+ * to add or withdraw this feature at runtime.
+ * See groovy/lang/InterceptorTest.groovy for details.
+ * <p>
+ * WARNING: This implementation of ProxyMetaClass is NOT thread-safe and hence should only be used for
+ * as a per-instance MetaClass running in a single thread. Do not place this MetaClass in the MetaClassRegistry
+ * as it will result in unpredictable behaviour
+ *
+ * @author Dierk Koenig
+ * @author Graeme Rocher
+ * @see groovy.lang.MetaClassRegistry
+ */
+public class ProxyMetaClass extends MetaClassImpl implements AdaptingMetaClass {
+
+ protected MetaClass adaptee = null;
+ protected Interceptor interceptor = null;
+
+
+ /**
+ * convenience factory method for the most usual case.
+ */
+ public static ProxyMetaClass getInstance(Class theClass) {
+ MetaClassRegistry metaRegistry = GroovySystem.getMetaClassRegistry();
+ MetaClass meta = metaRegistry.getMetaClass(theClass);
+ return new ProxyMetaClass(metaRegistry, theClass, meta);
+ }
+
+ /**
+ * @param adaptee the MetaClass to decorate with interceptability
+ */
+ public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) {
+ super(registry, theClass);
+ this.adaptee = adaptee;
+ if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null");
+ super.initialize();
+ }
+
+ public synchronized void initialize() {
+ this.adaptee.initialize();
+ }
+
+ /**
+ * Use the ProxyMetaClass for the given Closure.
+ * Cares for balanced register/unregister.
+ *
+ * @param closure piece of code to be executed with registered ProxyMetaClass
+ */
+ public Object use(Closure closure) {
+ // grab existing meta (usually adaptee but we may have nested use calls)
+ MetaClass origMetaClass = registry.getMetaClass(theClass);
+ registry.setMetaClass(theClass, this);
+ try {
+ return closure.call();
+ } finally {
+ registry.setMetaClass(theClass, origMetaClass);
+ }
+ }
+
+ /**
+ * Use the ProxyMetaClass for the given Closure.
+ * Cares for balanced setting/unsetting ProxyMetaClass.
+ *
+ * @param closure piece of code to be executed with ProxyMetaClass
+ */
+ public Object use(GroovyObject object, Closure closure) {
+ // grab existing meta (usually adaptee but we may have nested use calls)
+ MetaClass origMetaClass = object.getMetaClass();
+ object.setMetaClass(this);
+ try {
+ return closure.call();
+ } finally {
+ object.setMetaClass(origMetaClass);
+ }
+ }
+
+ /**
+ * @return the interceptor in use or null if no interceptor is used
+ */
+ public Interceptor getInterceptor() {
+ return interceptor;
+ }
+
+ /**
+ * @param interceptor may be null to reset any interception
+ */
+ public void setInterceptor(Interceptor interceptor) {
+ this.interceptor = interceptor;
+ }
+
+ /**
+ * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
+ * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+ * The method call is suppressed if Interceptor.doInvoke() returns false.
+ * See Interceptor for details.
+ */
+ public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
+ return doCall(object, methodName, arguments, interceptor, new Callable() {
+ public Object call() {
+ return adaptee.invokeMethod(object, methodName, arguments);
+ }
+ });
+ }
+
+ /**
+ * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
+ * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+ * The method call is suppressed if Interceptor.doInvoke() returns false.
+ * See Interceptor for details.
+ */
+ @Override
+ public Object invokeMethod(final Class sender, final Object object, final String methodName, final Object[] arguments, final boolean isCallToSuper, final boolean fromInsideClass) {
+ return doCall(object, methodName, arguments, interceptor, new Callable() {
+ public Object call() {
+ return adaptee.invokeMethod(sender, object, methodName, arguments, isCallToSuper, fromInsideClass);
+ }
+ });
+ }
+
+ /**
+ * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
+ * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+ * The method call is suppressed if Interceptor.doInvoke() returns false.
+ * See Interceptor for details.
+ */
+ public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
+ return doCall(object, methodName, arguments, interceptor, new Callable() {
+ public Object call() {
+ return adaptee.invokeStaticMethod(object, methodName, arguments);
+ }
+ });
+ }
+
+ /**
+ * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
+ * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+ * The method call is suppressed if Interceptor.doInvoke() returns false.
+ * See Interceptor for details.
+ */
+ public Object invokeConstructor(final Object[] arguments) {
+ return doCall(theClass, "ctor", arguments, interceptor, new Callable() {
+ public Object call() {
+ return adaptee.invokeConstructor(arguments);
+ }
+ });
+ }
+
+ /**
+ * Interceptors the call to getProperty if a PropertyAccessInterceptor is
+ * available
+ *
+ * @param object the object to invoke the getter on
+ * @param property the property name
+ * @return the value of the property
+ */
+ public Object getProperty(Class aClass, Object object, String property, boolean b, boolean b1) {
+ if (null == interceptor) {
+ return super.getProperty(aClass, object, property, b, b1);
+ }
+ if (interceptor instanceof PropertyAccessInterceptor) {
+ PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor;
+
+ Object result = pae.beforeGet(object, property);
+ if (interceptor.doInvoke()) {
+ result = super.getProperty(aClass, object, property, b, b1);
+ }
+ return result;
+ }
+ return super.getProperty(aClass, object, property, b, b1);
+ }
+
+ /**
+ * Interceptors the call to a property setter if a PropertyAccessInterceptor
+ * is available
+ *
+ * @param object The object to invoke the setter on
+ * @param property The property name to set
+ * @param newValue The new value of the property
+ */
+ public void setProperty(Class aClass, Object object, String property, Object newValue, boolean b, boolean b1) {
+ if (null == interceptor) {
+ super.setProperty(aClass, object, property, newValue, b, b1);
+ }
+ if (interceptor instanceof PropertyAccessInterceptor) {
+ PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor;
+
+ pae.beforeSet(object, property, newValue);
+ if (interceptor.doInvoke()) {
+ super.setProperty(aClass, object, property, newValue, b, b1);
+ }
+ } else {
+ super.setProperty(aClass, object, property, newValue, b, b1);
+ }
+ }
+
+ public MetaClass getAdaptee() {
+ return this.adaptee;
+ }
+
+ public void setAdaptee(MetaClass metaClass) {
+ this.adaptee = metaClass;
+ }
+
+ // since Java has no Closures...
+ private interface Callable {
+ Object call();
+ }
+
+ private Object doCall(Object object, String methodName, Object[] arguments, Interceptor interceptor, Callable howToInvoke) {
+ if (null == interceptor) {
+ return howToInvoke.call();
+ }
+ Object result = interceptor.beforeInvoke(object, methodName, arguments);
+ if (interceptor.doInvoke()) {
+ result = howToInvoke.call();
+ }
+ result = interceptor.afterInvoke(object, methodName, arguments, result);
+ return result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Range.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Range.java b/src/main/groovy/groovy/lang/Range.java
new file mode 100644
index 0000000..99f30de
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Range.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 java.util.List;
+
+/**
+ * A Range represents the list of discrete items between some starting (or <code>from</code>)
+ * value and <em>working up</em> towards some ending (or <code>to</code>) value.
+ * For a reverse range, the list is obtained by starting at the <code>to</code> value and
+ * <em>working down</em> towards the <code>from</code> value.
+ *
+ * The concept of <em>working up</em> and <em>working down</em> is dependent on the range implementation.
+ * In the general case, working up involves successive calls to the first item's <code>next()</code>
+ * method while working down involves calling the <code>previous()</code> method. Optimized
+ * numerical ranges may apply numeric addition or subtraction of some numerical step size.
+ *
+ * Particular range implementations may also support the notion of inclusivity
+ * and exclusivity with respect to the ending value in the range.
+ * E.g. <code>1..3 == [1, 2, 3]</code>; but <code>1..<3 == [1, 2]</code>.
+ *
+ * In general, the second boundary may not be contained in the range,
+ * and <code>a..b</code> may produce a different set of elements than <code>(b..a).reversed()</code>.
+ * E.g. <code>1..2.5 == [1, 2]</code>; but <code>2.5..1 == [2.5, 1.5]</code>.
+ *
+ * Implementations can be memory efficient by storing just the <code>from</code> and <code>to</code> boundary
+ * values rather than eagerly creating all discrete items in the conceptual list. The actual discrete items
+ * can be lazily calculated on an as needed basis (e.g. when calling methods from the <code>java.util.List</code>
+ * interface or the additional <code>step</code> methods in the <code>Range</code> interface).
+ *
+ * In addition to the methods related to a Range's "discrete items" abstraction, there is a method,
+ * <code>containsWithinBounds</code> which, for numerical ranges, allows checking within the continuous
+ * interval between the Range's boundary values.
+ */
+public interface Range<T extends Comparable> extends List<T> {
+ /**
+ * The lower value in the range.
+ *
+ * @return the lower value in the range.
+ */
+ T getFrom();
+
+ /**
+ * The upper value in the range.
+ *
+ * @return the upper value in the range
+ */
+ T getTo();
+
+ /**
+ * Indicates whether this is a reverse range which iterates backwards
+ * starting from the to value and ending on the from value
+ *
+ * @return <code>true</code> if this is a reverse range
+ */
+ boolean isReverse();
+
+ /**
+ * Indicates whether an object is greater than or equal to the <code>from</code>
+ * value for the range and less than or equal to the <code>to</code> value.
+ * <p>
+ * This may be true even for values not contained in the range.
+ *
+ * Example: from = 1.5, to = 3, next() increments by 1
+ * containsWithinBounds(2) == true
+ * contains(2) == false
+ *
+ * @param o the object to check against the boundaries of the range
+ * @return <code>true</code> if the object is between the from and to values
+ */
+ boolean containsWithinBounds(Object o);
+
+ /**
+ * Steps through the range, calling a closure for each item.
+ *
+ * @param step the amount by which to step. If negative, steps through the range backwards.
+ * @param closure the {@link Closure} to call
+ */
+ void step(int step, Closure closure);
+
+ /**
+ * Forms a list by stepping through the range by the indicated interval.
+ *
+ * @param step the amount by which to step. If negative, steps through the range backwards.
+ * @return the list formed by stepping through the range by the indicated interval.
+ */
+ List<T> step(int step);
+
+ /**
+ * @return the verbose {@link String} representation of this {@link Range} as would be typed into a console to create the {@link Range} instance
+ */
+ String inspect();
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ReadOnlyPropertyException.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ReadOnlyPropertyException.java b/src/main/groovy/groovy/lang/ReadOnlyPropertyException.java
new file mode 100644
index 0000000..342a182
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ReadOnlyPropertyException.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+
+/**
+ * This exception is thrown if an attempt is made to set a read only property
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class ReadOnlyPropertyException extends MissingPropertyException {
+
+ public ReadOnlyPropertyException(final String property, final Class type) {
+ super("Cannot set readonly property: " + property + " for class: " + type.getName(), property, type);
+ }
+
+ public ReadOnlyPropertyException(final String property, final String classname) {
+ super("Cannot set readonly property: " + property + " for class: " + classname);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Reference.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Reference.java b/src/main/groovy/groovy/lang/Reference.java
new file mode 100644
index 0000000..c4bf21d
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Reference.java
@@ -0,0 +1,82 @@
+/*
+ * 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 java.io.Serializable;
+
+/**
+ * Represents a reference to a value
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class Reference<T> extends GroovyObjectSupport implements Serializable {
+
+ private static final long serialVersionUID = 4963704631487573488L;
+ private T value;
+
+ public Reference() {
+ }
+
+ public Reference(T value) {
+ this.value = value;
+ }
+
+ public Object getProperty(String property) {
+ Object value = get();
+ if (value != null) {
+ return InvokerHelper.getProperty(value, property);
+ }
+ return super.getProperty(property);
+ }
+
+ public void setProperty(String property, Object newValue) {
+ Object value = get();
+ if (value != null) {
+ InvokerHelper.setProperty(value, property, newValue);
+ }
+ else {
+ super.setProperty(property, newValue);
+ }
+ }
+
+ public Object invokeMethod(String name, Object args) {
+ Object value = get();
+ if (value != null) {
+ try {
+ return InvokerHelper.invokeMethod(value, name, args);
+ }
+ catch (Exception e) {
+ return super.invokeMethod(name, args);
+ }
+ }
+ else {
+ return super.invokeMethod(name, args);
+ }
+ }
+
+ public T get() {
+ return value;
+ }
+
+ public void set(T value) {
+ this.value = value;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Script.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Script.java b/src/main/groovy/groovy/lang/Script.java
new file mode 100644
index 0000000..196c74c
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Script.java
@@ -0,0 +1,231 @@
+/*
+ * 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.expr.ArgumentListExpression;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This object represents a Groovy script
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ */
+public abstract class Script extends GroovyObjectSupport {
+ private Binding binding;
+
+ protected Script() {
+ this(new Binding());
+ }
+
+ protected Script(Binding binding) {
+ this.binding = binding;
+ }
+
+ public Binding getBinding() {
+ return binding;
+ }
+
+ public void setBinding(Binding binding) {
+ this.binding = binding;
+ }
+
+ public Object getProperty(String property) {
+ try {
+ return binding.getVariable(property);
+ } catch (MissingPropertyException e) {
+ return super.getProperty(property);
+ }
+ }
+
+ public void setProperty(String property, Object newValue) {
+ if ("binding".equals(property))
+ setBinding((Binding) newValue);
+ else if("metaClass".equals(property))
+ setMetaClass((MetaClass)newValue);
+ else
+ binding.setVariable(property, newValue);
+ }
+
+ /**
+ * Invoke a method (or closure in the binding) defined.
+ *
+ * @param name method to call
+ * @param args arguments to pass to the method
+ * @return value
+ */
+ public Object invokeMethod(String name, Object args) {
+ try {
+ return super.invokeMethod(name, args);
+ }
+ // if the method was not found in the current scope (the script's methods)
+ // let's try to see if there's a method closure with the same name in the binding
+ catch (MissingMethodException mme) {
+ try {
+ if (name.equals(mme.getMethod())) {
+ Object boundClosure = getProperty(name);
+ if (boundClosure != null && boundClosure instanceof Closure) {
+ return ((Closure) boundClosure).call((Object[])args);
+ } else {
+ throw mme;
+ }
+ } else {
+ throw mme;
+ }
+ } catch (MissingPropertyException mpe) {
+ throw mme;
+ }
+ }
+ }
+
+ /**
+ * The main instance method of a script which has variables in scope
+ * as defined by the current {@link Binding} instance.
+ */
+ public abstract Object run();
+
+ // println helper methods
+
+ /**
+ * Prints a newline to the current 'out' variable which should be a PrintWriter
+ * or at least have a println() method defined on it.
+ * If there is no 'out' property then print to standard out.
+ */
+ public void println() {
+ Object object;
+
+ try {
+ object = getProperty("out");
+ } catch (MissingPropertyException e) {
+ System.out.println();
+ return;
+ }
+
+ InvokerHelper.invokeMethod(object, "println", ArgumentListExpression.EMPTY_ARRAY);
+ }
+
+ /**
+ * Prints the value to the current 'out' variable which should be a PrintWriter
+ * or at least have a print() method defined on it.
+ * If there is no 'out' property then print to standard out.
+ */
+ public void print(Object value) {
+ Object object;
+
+ try {
+ object = getProperty("out");
+ } catch (MissingPropertyException e) {
+ DefaultGroovyMethods.print(System.out,value);
+ return;
+ }
+
+ InvokerHelper.invokeMethod(object, "print", new Object[]{value});
+ }
+
+ /**
+ * Prints the value and a newline to the current 'out' variable which should be a PrintWriter
+ * or at least have a println() method defined on it.
+ * If there is no 'out' property then print to standard out.
+ */
+ public void println(Object value) {
+ Object object;
+
+ try {
+ object = getProperty("out");
+ } catch (MissingPropertyException e) {
+ DefaultGroovyMethods.println(System.out,value);
+ return;
+ }
+
+ InvokerHelper.invokeMethod(object, "println", new Object[]{value});
+ }
+
+ /**
+ * Prints a formatted string using the specified format string and argument.
+ *
+ * @param format the format to follow
+ * @param value the value to be formatted
+ */
+ public void printf(String format, Object value) {
+ Object object;
+
+ try {
+ object = getProperty("out");
+ } catch (MissingPropertyException e) {
+ DefaultGroovyMethods.printf(System.out, format, value);
+ return;
+ }
+
+ InvokerHelper.invokeMethod(object, "printf", new Object[] { format, value });
+ }
+
+ /**
+ * Prints a formatted string using the specified format string and arguments.
+ *
+ * @param format the format to follow
+ * @param values an array of values to be formatted
+ */
+ public void printf(String format, Object[] values) {
+ Object object;
+
+ try {
+ object = getProperty("out");
+ } catch (MissingPropertyException e) {
+ DefaultGroovyMethods.printf(System.out, format, values);
+ return;
+ }
+
+ InvokerHelper.invokeMethod(object, "printf", new Object[] { format, values });
+ }
+
+ /**
+ * A helper method to allow the dynamic evaluation of groovy expressions using this
+ * scripts binding as the variable scope
+ *
+ * @param expression is the Groovy script expression to evaluate
+ */
+ public Object evaluate(String expression) throws CompilationFailedException {
+ GroovyShell shell = new GroovyShell(getClass().getClassLoader(), binding);
+ return shell.evaluate(expression);
+ }
+
+ /**
+ * A helper method to allow the dynamic evaluation of groovy expressions using this
+ * scripts binding as the variable scope
+ *
+ * @param file is the Groovy script to evaluate
+ */
+ public Object evaluate(File file) throws CompilationFailedException, IOException {
+ GroovyShell shell = new GroovyShell(getClass().getClassLoader(), binding);
+ return shell.evaluate(file);
+ }
+
+ /**
+ * A helper method to allow scripts to be run taking command line arguments
+ */
+ public void run(File file, String[] arguments) throws CompilationFailedException, IOException {
+ GroovyShell shell = new GroovyShell(getClass().getClassLoader(), binding);
+ shell.run(file, arguments);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Sequence.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Sequence.java b/src/main/groovy/groovy/lang/Sequence.java
new file mode 100644
index 0000000..2b4316c
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Sequence.java
@@ -0,0 +1,224 @@
+/*
+ * 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.typehandling.DefaultTypeTransformation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents a sequence of objects which represents zero 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:james@coredevelopers.net">James Strachan</a>
+ */
+public class Sequence extends ArrayList implements GroovyObject {
+
+ private MetaClass metaClass = InvokerHelper.getMetaClass(getClass());
+ private final Class type;
+ private int hashCode;
+
+ public Sequence() {
+ this(null);
+ }
+
+ public Sequence(Class type) {
+ this.type = type;
+ }
+
+ public Sequence(Class type, List content) {
+ super(content.size());
+ this.type = type;
+ addAll(content);
+ }
+
+ /**
+ * Sets the contents of this sequence to that
+ * of the given collection.
+ */
+ public void set(Collection collection) {
+ checkCollectionType(collection);
+ clear();
+ addAll(collection);
+ }
+
+ public boolean equals(Object that) {
+ if (that instanceof Sequence) {
+ return equals((Sequence) that);
+ }
+ return false;
+ }
+
+ public boolean equals(Sequence that) {
+ if (size() == that.size()) {
+ for (int i = 0; i < size(); i++) {
+ if (!DefaultTypeTransformation.compareEqual(this.get(i), that.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ if (hashCode == 0) {
+ for (int i = 0; i < size(); i++) {
+ Object value = get(i);
+ int hash = (value != null) ? value.hashCode() : 0xbabe;
+ hashCode ^= hash;
+ }
+ if (hashCode == 0) {
+ hashCode = 0xbabe;
+ }
+ }
+ return hashCode;
+ }
+
+ public int minimumSize() {
+ return 0;
+ }
+
+ /**
+ * @return the type of the elements in the sequence or null if there is no
+ * type constraint on this sequence
+ */
+ public Class type() {
+ return type;
+ }
+
+ public void add(int index, Object element) {
+ checkType(element);
+ hashCode = 0;
+ super.add(index, element);
+ }
+
+ public boolean add(Object element) {
+ checkType(element);
+ hashCode = 0;
+ return super.add(element);
+ }
+
+ public boolean addAll(Collection c) {
+ checkCollectionType(c);
+ hashCode = 0;
+ return super.addAll(c);
+ }
+
+ public boolean addAll(int index, Collection c) {
+ checkCollectionType(c);
+ hashCode = 0;
+ return super.addAll(index, c);
+ }
+
+ public void clear() {
+ hashCode = 0;
+ super.clear();
+ }
+
+ public Object remove(int index) {
+ hashCode = 0;
+ return super.remove(index);
+ }
+
+ protected void removeRange(int fromIndex, int toIndex) {
+ hashCode = 0;
+ super.removeRange(fromIndex, toIndex);
+ }
+
+ public Object set(int index, Object element) {
+ hashCode = 0;
+ return super.set(index, element);
+ }
+
+ // GroovyObject interface
+ //-------------------------------------------------------------------------
+ public Object invokeMethod(String name, Object args) {
+ try {
+ return getMetaClass().invokeMethod(this, name, args);
+ }
+ catch (MissingMethodException e) {
+ // lets apply the method to each item in the collection
+ List answer = new ArrayList(size());
+ for (Iterator iter = iterator(); iter.hasNext(); ) {
+ Object element = iter.next();
+ Object value = InvokerHelper.invokeMethod(element, name, args);
+ answer.add(value);
+ }
+ return answer;
+ }
+ }
+
+ public Object getProperty(String property) {
+ return getMetaClass().getProperty(this, property);
+ }
+
+ public void setProperty(String property, Object newValue) {
+ getMetaClass().setProperty(this, property, newValue);
+ }
+
+ public MetaClass getMetaClass() {
+ return metaClass;
+ }
+
+ public void setMetaClass(MetaClass metaClass) {
+ this.metaClass = metaClass;
+ }
+
+ // Implementation methods
+ //-------------------------------------------------------------------------
+
+ /**
+ * Checks that each member of the given collection are of the correct
+ * type
+ */
+ protected void checkCollectionType(Collection c) {
+ if (type != null) {
+ for (Iterator iter = c.iterator(); iter.hasNext(); ) {
+ Object element = iter.next();
+ checkType(element);
+ }
+ }
+ }
+
+
+ /**
+ * Checks that the given object instance is of the correct type
+ * otherwise a runtime exception is thrown
+ */
+ protected void checkType(Object object) {
+ if (object == null) {
+ throw new NullPointerException("Sequences cannot contain null, use a List instead");
+ }
+ if (type != null) {
+ if (!type.isInstance(object)) {
+ throw new IllegalArgumentException(
+ "Invalid type of argument for sequence of type: "
+ + type.getName()
+ + " cannot add object: "
+ + object);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Singleton.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Singleton.java b/src/main/groovy/groovy/lang/Singleton.java
new file mode 100644
index 0000000..9b86c8b
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Singleton.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+/**
+ * Class annotation to make a singleton class. The singleton is obtained through normal property access using the singleton property (defaults to "instance").
+ *
+ * Such classes can be initialized during normal static initialization of the class or lazily (on first access).
+ * To make the singleton lazy use {@code @Singleton(lazy=true)}.
+ * Lazy singletons are implemented with double-checked locking and a volatile backing field.
+ * By default, no explicit constructors are allowed. To create one or more explicit constructors
+ * use {@code @Singleton(strict=false)}.
+ * This could be used to:
+ * <ul>
+ * <li>provide your own custom initialization logic in your own no-arg constructor - you
+ * will be responsible for the entire code (the {@code @Singleton} annotation becomes merely documentation)</li>
+ * <li>provide one or more constructors with arguments for a quasi-singleton - these constructors will be used
+ * to create instances that are independent of the singleton instance returned by the singleton property</li>
+ * </ul>
+ *
+ * @author Alex Tkachman
+ * @author Paul King
+ */
+@java.lang.annotation.Documented
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.TYPE})
+@GroovyASTTransformationClass("org.codehaus.groovy.transform.SingletonASTTransformation")
+public @interface Singleton {
+ /**
+ * @return if this singleton should be lazy
+ */
+ boolean lazy() default false;
+
+ /**
+ * @return if this singleton should have strict semantics
+ */
+ boolean strict() default true;
+
+ /**
+ * @return the singleton property name
+ */
+ String property() default "instance";
+}