From 952045c216d5d95e0dcfed346b6f06f8303bd13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Le=C5=9Bkiewicz?= Date: Tue, 8 Jan 2019 22:24:15 +0100 Subject: [PATCH] SPR-17606 @Profile mishandles "not" operand mixed with "&" (#2066) Correct handling of not/and expressions in ProfilesParser Issue: SPR-17606 --- .../core/env/ProfilesParser.java | 25 +++++++-- .../core/env/ProfilesTests.java | 52 ++++++++++++++++++- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java b/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java index f724a8c1e69..1ca396b5831 100644 --- a/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java +++ b/spring-core/src/main/java/org/springframework/core/env/ProfilesParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,6 +54,9 @@ final class ProfilesParser { } private static Profiles parseTokens(String expression, StringTokenizer tokens) { + return parseTokens(expression, tokens, Context.NONE); + } + private static Profiles parseTokens(String expression, StringTokenizer tokens, Context context) { List elements = new ArrayList<>(); Operator operator = null; while (tokens.hasMoreTokens()) { @@ -63,7 +66,11 @@ final class ProfilesParser { } switch (token) { case "(": - elements.add(parseTokens(expression, tokens)); + Profiles contents = parseTokens(expression, tokens, Context.BRACKET); + if (context == Context.INVERT) { + return contents; + } + elements.add(contents); break; case "&": assertWellFormed(expression, operator == null || operator == Operator.AND); @@ -74,16 +81,23 @@ final class ProfilesParser { operator = Operator.OR; break; case "!": - elements.add(not(parseTokens(expression, tokens))); + elements.add(not(parseTokens(expression, tokens, Context.INVERT))); break; case ")": Profiles merged = merge(expression, elements, operator); + if (context == Context.BRACKET) { + return merged; + } elements.clear(); elements.add(merged); operator = null; break; default: - elements.add(equals(token)); + Profiles value = equals(token); + if (context == Context.INVERT) { + return value; + } + elements.add(value); } } return merge(expression, elements, operator); @@ -126,6 +140,9 @@ final class ProfilesParser { private enum Operator {AND, OR} + private enum Context {NONE, INVERT, BRACKET} + + private static class ParsedProfiles implements Profiles { private final String[] expressions; diff --git a/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java b/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java index b6a314ed15b..8c1a2e0c1f8 100644 --- a/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java +++ b/spring-core/src/test/java/org/springframework/core/env/ProfilesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -195,6 +195,56 @@ public class ProfilesTests { assertTrue(profiles.matches(activeProfiles("java"))); } + @Test + public void ofAndExpressionWithInvertedSingleElement() { + Profiles profiles = Profiles.of("!spring & framework"); + assertOfAndExpressionWithInvertedSingleElement(profiles); + } + + @Test + public void ofAndExpressionWithInBracketsInvertedSingleElement() { + Profiles profiles = Profiles.of("(!spring) & framework"); + assertOfAndExpressionWithInvertedSingleElement(profiles); + } + + @Test + public void ofAndExpressionWithInvertedSingleElementInBrackets() { + Profiles profiles = Profiles.of("! (spring) & framework"); + assertOfAndExpressionWithInvertedSingleElement(profiles); + } + + @Test + public void ofAndExpressionWithInvertedSingleElementInBracketsWithoutSpaces() { + Profiles profiles = Profiles.of("!(spring)&framework"); + assertOfAndExpressionWithInvertedSingleElement(profiles); + } + + @Test + public void ofAndExpressionWithInvertedSingleElementWithoutSpaces() { + Profiles profiles = Profiles.of("!spring&framework"); + assertOfAndExpressionWithInvertedSingleElement(profiles); + } + + private void assertOfAndExpressionWithInvertedSingleElement(Profiles profiles) { + assertTrue(profiles.matches(activeProfiles("framework"))); + assertFalse(profiles.matches(activeProfiles("java"))); + assertFalse(profiles.matches(activeProfiles("spring", "framework"))); + assertFalse(profiles.matches(activeProfiles("spring"))); + } + + @Test + public void ofOrExpressionWithInvertedSingleElementWithoutSpaces() { + Profiles profiles = Profiles.of("!spring|framework"); + assertOfOrExpressionWithInvertedSingleElement(profiles); + } + + private void assertOfOrExpressionWithInvertedSingleElement(Profiles profiles) { + assertTrue(profiles.matches(activeProfiles("framework"))); + assertTrue(profiles.matches(activeProfiles("java"))); + assertTrue(profiles.matches(activeProfiles("spring", "framework"))); + assertFalse(profiles.matches(activeProfiles("spring"))); + } + @Test public void ofNotOrExpression() { Profiles profiles = Profiles.of("!(spring | framework)");