Skip to content

Commit d66fd8d

Browse files
authored
Merge branch 'apache:master' into master
2 parents bfaa0d5 + 5916bc5 commit d66fd8d

7 files changed

Lines changed: 850 additions & 112 deletions

File tree

src/main/java/groovy/lang/MetaClassImpl.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@
110110
import java.util.concurrent.ConcurrentHashMap;
111111
import java.util.concurrent.ConcurrentMap;
112112
import java.util.function.BiConsumer;
113-
import java.util.regex.Pattern;
114113

115114
import static groovy.lang.Tuple.tuple;
116115
import static java.lang.Character.isUpperCase;
@@ -145,11 +144,6 @@ public class MetaClassImpl implements MetaClass, MutableMetaClass {
145144
private static final MetaMethod AMBIGUOUS_LISTENER_METHOD = new DummyMetaMethod();
146145
private static final Comparator<CachedClass> CACHED_CLASS_NAME_COMPARATOR = Comparator.comparing(CachedClass::getName);
147146
private static final boolean PERMISSIVE_PROPERTY_ACCESS = SystemUtil.getBooleanSafe("groovy.permissive.property.access");
148-
private static final Pattern TRAIT_FIELD;
149-
static {
150-
final var identifier = "[\\p{javaJavaIdentifierStart}&&[^_$]][\\p{javaJavaIdentifierPart}&&[^_$]]*"; // _ and $ are special
151-
TRAIT_FIELD = Pattern.compile("(?:" + identifier + "_)*" + identifier + "(?:\\$" + identifier + ")*__(" + identifier + ")");
152-
}
153147
private static final VMPlugin VM_PLUGIN = VMPluginFactory.getPlugin();
154148

155149
protected final Class theClass;
@@ -2366,12 +2360,6 @@ private void fillStaticPropertyIndex() {
23662360
}
23672361
if (prop != null) {
23682362
staticPropertyIndex.put(name, prop);
2369-
if (!name.startsWith("$") && name.contains("__")) {
2370-
var matcher = TRAIT_FIELD.matcher(name);
2371-
if (matcher.matches()) { // GROOVY-11641
2372-
staticPropertyIndex.putIfAbsent(matcher.group(1), prop);
2373-
}
2374-
}
23752363
}
23762364
};
23772365

src/main/java/org/codehaus/groovy/runtime/ArrayGroovyMethods.java

Lines changed: 594 additions & 47 deletions
Large diffs are not rendered by default.

src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4094,6 +4094,31 @@ public static void downto(BigDecimal self, Number to, @ClosureParams(FirstParam.
40944094
") to downto() cannot be greater than the value (" + self + ") it's called on."); }
40954095
}
40964096

4097+
//--------------------------------------------------------------------------
4098+
// drain
4099+
4100+
/**
4101+
* Drain the queue of elements, returning them as a list.
4102+
* <pre class="groovyTestCase">
4103+
* def letters = new PriorityQueue(String.CASE_INSENSITIVE_ORDER)
4104+
* letters.addAll(['Z', 'y', 'X', 'a', 'B', 'c'])
4105+
* assert letters.toList() == ['a', 'B', 'c', 'Z', 'X', 'y']
4106+
* assert letters.drain() == ['a', 'B', 'c', 'X', 'y', 'Z']
4107+
* assert letters.empty
4108+
* </pre>
4109+
*
4110+
* @param self a Queue
4111+
* @return a List of elements removed from the head of the queue
4112+
* @since 5.0.0
4113+
*/
4114+
public static <T> List<T> drain(Queue<T> self) {
4115+
List<T> answer = new ArrayList<>();
4116+
while (!self.isEmpty()) {
4117+
answer.add(self.poll());
4118+
}
4119+
return answer;
4120+
}
4121+
40974122
//--------------------------------------------------------------------------
40984123
// drop
40994124

@@ -13430,7 +13455,7 @@ public static <T> Iterator<T> sort(Iterator<T> self, Comparator<? super T> compa
1343013455
* </pre>
1343113456
*
1343213457
* @param self the Iterable to be sorted
13433-
* @param mutate false will always cause a new list to be created, true will mutate lists in place
13458+
* @param mutate false causes a new list to be created, true will mutate lists in place
1343413459
* @param comparator a Comparator used for the comparison
1343513460
* @return a sorted List
1343613461
* @since 2.2.0
@@ -13441,6 +13466,78 @@ public static <T> List<T> sort(Iterable<T> self, boolean mutate, Comparator<? su
1344113466
return list;
1344213467
}
1344313468

13469+
/**
13470+
* Sorts elements in the given index range using the given Closure to determine the ordering.
13471+
* If mutate is true, it is sorted in place and returned. Otherwise, the elements are first placed
13472+
* into a new list which is then sorted and returned, leaving the original List unchanged.
13473+
*
13474+
* <pre class="groovyTestCase">
13475+
* // a list with some odd then even numbers
13476+
* def nums = [5, 9, 1, 7, 3, 4, 8, 6, 0, 2]
13477+
*
13478+
* // sort odds ascending, evens descending
13479+
* assert nums.sort(0..4, false) { it }.sort(5..9, false) { -it }
13480+
* == [1, 3, 5, 7, 9, 8, 6, 4, 2, 0]
13481+
* // sort odds descending, evens descending
13482+
* assert nums.sort(0..&lt;5, false) { -it }.sort(4&lt;..&lt;10, false) { -it }
13483+
* == [9, 7, 5, 3, 1, 8, 6, 4, 2, 0]
13484+
* // sort odds descending, evens ascending
13485+
* assert nums.sort(0..&lt;5, false) { -it }.sort(5..-1, false) { it }
13486+
* == [9, 7, 5, 3, 1, 0, 2, 4, 6, 8]
13487+
* // leave first and last numbers, sort remaining odds ascending, remaining evens descending
13488+
* assert nums.sort(1..4, false) { it }.sort(5..-2, false) { -it }
13489+
* == [5, 1, 3, 7, 9, 8, 6, 4, 0, 2]
13490+
* // leave first and last numbers, sort remaining odds descending, remaining evens ascending
13491+
* assert nums.sort(1..4, false) { -it }.sort(5..-2, false) { it }
13492+
* == [5, 9, 7, 3, 1, 0, 4, 6, 8, 2]
13493+
* // leave first and last odds and evens, sort remaining odds ascending, remaining evens descending
13494+
* assert nums.sort(0&lt;..&lt;4, false) { it }.sort(5&lt;..&lt;9, false) { -it }
13495+
* == [5, 1, 7, 9, 3, 4, 8, 6, 0, 2]
13496+
* // leave first and last odds and evens, sort remaining odds descending, remaining evens ascending
13497+
* assert nums.sort(0&lt;..&lt;4, false) { -it }.sort(5&lt;..&lt;-1, false) { it }
13498+
* == [5, 9, 7, 1, 3, 4, 0, 6, 8, 2]
13499+
* </pre>
13500+
*
13501+
* @param self the List to be sorted
13502+
* @param range the inclusive range of index values over which to sort
13503+
* @param mutate false causes a new list to be created, true will mutate lists in place
13504+
* @param closure a Closure used to determine the correct ordering
13505+
* @return a sorted List
13506+
* @since 5.0.0
13507+
*/
13508+
public static <T> List<T> sort(List<T> self, IntRange range, boolean mutate, @ClosureParams(value=FromString.class,options={"T","T,T"}) Closure<?> closure) {
13509+
Objects.requireNonNull(self);
13510+
RangeInfo info = range.subListBorders(self.size());
13511+
Objects.checkFromToIndex(info.from, info.to, self.size());
13512+
T[] a = (T[]) self.toArray();
13513+
Comparator<T> c = closure.getMaximumNumberOfParameters() == 1 ? new OrderBy<>(closure) : new ClosureComparator<>(closure);
13514+
Arrays.sort(a, info.from, info.to, c);
13515+
if (!mutate) {
13516+
return Arrays.asList(a);
13517+
}
13518+
ListIterator<T> i = self.listIterator();
13519+
for (T e : a) {
13520+
i.next();
13521+
i.set(e);
13522+
}
13523+
return self;
13524+
}
13525+
13526+
/**
13527+
* A sort variant that takes an index range and always mutates the original list.
13528+
* <pre class="groovyTestCase">
13529+
* def nums = [5, 9, 1, 7, 3, 4, 8, 6, 0, 2]
13530+
* nums.sort(0..4) { it }
13531+
* assert nums == [1, 3, 5, 7, 9, 4, 8, 6, 0, 2]
13532+
* </pre>
13533+
*
13534+
* @see #sort(List, IntRange, boolean, Closure)
13535+
* @return the sorted list
13536+
*/
13537+
public static <T> List<T> sort(List<T> self, IntRange range, @ClosureParams(value=FromString.class,options={"T","T,T"}) Closure<?> closure) {
13538+
return sort(self, range, true, closure);
13539+
}
13540+
1344413541
/**
1344513542
* Sorts the given iterator items into a sorted iterator using the Closure to determine the correct ordering.
1344613543
* The original iterator will be fully processed after the method call.
@@ -15202,6 +15299,20 @@ public static <T> Iterator<T> toSorted(Iterator<T> self, @ClosureParams(value=Fr
1520215299
return toSorted(self, comparator);
1520315300
}
1520415301

15302+
/**
15303+
* A sort variant that takes an index range and never modifies the original list.
15304+
* <pre class="groovyTestCase">
15305+
* def nums = [5, 9, 1, 7, 3, 4, 8, 6, 0, 2]
15306+
* assert nums.toSorted(0..4) { it } == [1, 3, 5, 7, 9, 4, 8, 6, 0, 2]
15307+
* </pre>
15308+
*
15309+
* @see #sort(List, IntRange, boolean, Closure)
15310+
* @return the sorted list
15311+
*/
15312+
public static <T> List<T> toSorted(List<T> self, IntRange range, @ClosureParams(value=FromString.class,options={"T","T,T"}) Closure<?> closure) {
15313+
return sort(self, range, false, closure);
15314+
}
15315+
1520515316
/**
1520615317
* Sorts the elements from the given map into a new ordered map using
1520715318
* a {@link NumberAwareComparator} on map entry values to determine the resulting order.

src/main/java/org/codehaus/groovy/runtime/StringGroovyMethods.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2854,6 +2854,48 @@ public static int size(final CharSequence self) {
28542854
return self.length();
28552855
}
28562856

2857+
/**
2858+
* Provides a {@code getLength} alias for {@code length} for {@link StringBuilder},
2859+
* supporting assignment arithmetic operator expressions involving
2860+
* the otherwise write-only length property.
2861+
*
2862+
* <pre class="groovyTestCase">
2863+
* def sb = new StringBuilder()
2864+
* sb << 'foobar'
2865+
* sb.length -= 3
2866+
* assert sb.toString() == 'foo'
2867+
* </pre>
2868+
*
2869+
* @param self a StringBuilder
2870+
* @return the length of the StringBuilder
2871+
*
2872+
* @since 5.0.0
2873+
*/
2874+
public static int getLength(final StringBuilder self) {
2875+
return self.length();
2876+
}
2877+
2878+
/**
2879+
* Provides a {@code getLength} alias for {@code length} for {@link StringBuffer},
2880+
* supporting assignment arithmetic operator expressions involving
2881+
* the otherwise write-only length property.
2882+
*
2883+
* <pre class="groovyTestCase">
2884+
* def sb = new StringBuffer()
2885+
* sb << 'foobar'
2886+
* sb.length -= 3
2887+
* assert sb.toString() == 'foo'
2888+
* </pre>
2889+
*
2890+
* @param self a StringBuffer
2891+
* @return the length of the StringBuffer
2892+
*
2893+
* @since 5.0.0
2894+
*/
2895+
public static int getLength(final StringBuffer self) {
2896+
return self.length();
2897+
}
2898+
28572899
/**
28582900
* Provides the standard Groovy {@code size()} method for {@code Matcher}.
28592901
*

src/main/java/org/codehaus/groovy/transform/trait/TraitReceiverTransformer.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import static org.codehaus.groovy.ast.tools.GeneralUtils.propX;
5656
import static org.codehaus.groovy.ast.tools.GeneralUtils.ternaryX;
5757
import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
58-
import static org.codehaus.groovy.transform.stc.StaticTypesMarker.DYNAMIC_RESOLUTION;
5958

6059
/**
6160
* This expression transformer is used internally by the {@link org.codehaus.groovy.transform.trait.TraitASTTransformation
@@ -122,9 +121,8 @@ public Expression transform(final Expression exp) {
122121
propertyExpression.getProperty().setSourcePosition(exp);
123122
return propertyExpression;
124123
}
125-
} else if (accessedVariable instanceof DynamicVariable && !inClosure) { // GROOVY-9386
124+
} else if (accessedVariable instanceof DynamicVariable && !inClosure) { // GROOVY-8049, GROOVY-9386
126125
PropertyExpression propertyExpression = propX(varX(weaved), vexp.getName());
127-
propertyExpression.putNodeMetaData(DYNAMIC_RESOLUTION, Boolean.TRUE);
128126
propertyExpression.getProperty().setSourcePosition(exp);
129127
return propertyExpression;
130128
}

src/test/groovy/groovy/util/logging/Slf4jTest.groovy

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,12 @@ import ch.qos.logback.classic.LoggerContext
2424
import ch.qos.logback.classic.spi.LoggingEvent
2525
import ch.qos.logback.core.OutputStreamAppender
2626
import ch.qos.logback.core.layout.EchoLayout
27-
import org.junit.After
28-
import org.junit.Before
29-
import org.junit.Test
27+
import groovy.test.NotYetImplemented
28+
import org.junit.jupiter.api.AfterEach
29+
import org.junit.jupiter.api.BeforeEach
30+
import org.junit.jupiter.api.Test
31+
import org.junit.jupiter.params.ParameterizedTest
32+
import org.junit.jupiter.params.provider.ValueSource
3033
import org.slf4j.LoggerFactory
3134

3235
import java.lang.reflect.Modifier
@@ -60,7 +63,7 @@ final class Slf4jTest {
6063
private LogbackInterceptingAppender appender
6164
private Logger logger
6265

63-
@Before
66+
@BeforeEach
6467
void setUp() {
6568
appender = new LogbackInterceptingAppender()
6669
appender.outputStream = new ByteArrayOutputStream()
@@ -74,7 +77,7 @@ final class Slf4jTest {
7477
logger.level = Level.ALL
7578
}
7679

77-
@After
80+
@AfterEach
7881
void tearDown() {
7982
logger.detachAppender(appender)
8083
}
@@ -293,7 +296,8 @@ final class Slf4jTest {
293296
assert events[ind].message == 'trace called'
294297
}
295298

296-
@Test // GROOVY-6373
299+
// GROOVY-6373
300+
@Test
297301
void testLogWithInnerClasses() {
298302
Class clazz = new GroovyClassLoader().parseClass('''
299303
@groovy.util.logging.Slf4j('logger')
@@ -323,7 +327,8 @@ final class Slf4jTest {
323327
assert events[ind].message == 'inner called'
324328
}
325329

326-
@Test // GROOVY-6834
330+
// GROOVY-6834
331+
@Test
327332
void testLogTransformInteractionWithAnonInnerClass() {
328333
assertScript '''
329334
@groovy.util.logging.Slf4j
@@ -344,7 +349,8 @@ final class Slf4jTest {
344349
'''
345350
}
346351

347-
@Test // GROOVY-6873
352+
// GROOVY-6873
353+
@Test
348354
void testLogTransformInteractionWithAnonInnerClass2() {
349355
Class clazz = new GroovyClassLoader().parseClass('''
350356
@groovy.util.logging.Slf4j
@@ -367,6 +373,29 @@ final class Slf4jTest {
367373
''')
368374
}
369375

376+
// GROOVY-7439
377+
@NotYetImplemented
378+
@ParameterizedTest
379+
@ValueSource(strings=[
380+
'log', // Cannot find matching method Object#debug(String)
381+
'((org.slf4j.Logger) log)' // No such property: log for class: C
382+
])
383+
void testLogTrait(String log) {
384+
assertScript """
385+
@groovy.transform.CompileStatic
386+
@groovy.util.logging.Slf4j
387+
trait T {
388+
void test() {
389+
${log}.debug('trace')
390+
}
391+
}
392+
class C implements T {
393+
}
394+
395+
new C().test()
396+
"""
397+
}
398+
370399
@Test
371400
void testLogGuard() {
372401
Class clazz = new GroovyClassLoader().parseClass('''

0 commit comments

Comments
 (0)