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 2018/11/02 16:40:38 UTC
groovy git commit: GROOVY-8866: Implement `GProperties` to handle
properties file smartly(closes #818)
Repository: groovy
Updated Branches:
refs/heads/master 57fe0c773 -> 5de34ce58
GROOVY-8866: Implement `GProperties` to handle properties file smartly(closes #818)
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/5de34ce5
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/5de34ce5
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/5de34ce5
Branch: refs/heads/master
Commit: 5de34ce58417ec48f0e1cdedfe78cb2600e9c913
Parents: 57fe0c7
Author: Daniel Sun <su...@apache.org>
Authored: Sat Nov 3 00:40:04 2018 +0800
Committer: Daniel Sun <su...@apache.org>
Committed: Sat Nov 3 00:40:04 2018 +0800
----------------------------------------------------------------------
src/main/groovy/groovy/lang/Binding.java | 13 ++
src/main/groovy/groovy/lang/GroovyShell.java | 4 +
src/main/groovy/groovy/util/GProperties.groovy | 204 +++++++++++++++++++
.../groovy/util/gproperties.properties | 28 +++
.../groovy/util/gproperties_import.properties | 21 ++
.../groovy/util/gproperties_import2.properties | 20 ++
.../groovy/util/gproperties_import3.properties | 20 ++
src/test/groovy/util/GPropertiesTest.groovy | 101 +++++++++
8 files changed, 411 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/main/groovy/groovy/lang/Binding.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Binding.java b/src/main/groovy/groovy/lang/Binding.java
index 6505ea5..add048c 100644
--- a/src/main/groovy/groovy/lang/Binding.java
+++ b/src/main/groovy/groovy/lang/Binding.java
@@ -77,6 +77,19 @@ public class Binding extends GroovyObjectSupport {
variables = new LinkedHashMap();
variables.put(name, value);
}
+
+ /**
+ * remove the variable with the specified name
+ *
+ * @param name the name of the variable to remove
+ */
+ public void removeVariable(String name) {
+ if (null == variables) {
+ return;
+ }
+
+ variables.remove(name);
+ }
/**
* Simple check for whether the binding contains a particular variable or not.
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/main/groovy/groovy/lang/GroovyShell.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyShell.java b/src/main/groovy/groovy/lang/GroovyShell.java
index 3570809..a802d3c 100644
--- a/src/main/groovy/groovy/lang/GroovyShell.java
+++ b/src/main/groovy/groovy/lang/GroovyShell.java
@@ -429,6 +429,10 @@ public class GroovyShell extends GroovyObjectSupport {
context.setVariable(name, value);
}
+ public void removeVariable(String name) {
+ context.removeVariable(name);
+ }
+
/**
* Evaluates some script against the current Binding and returns the result
*
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/main/groovy/groovy/util/GProperties.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/GProperties.groovy b/src/main/groovy/groovy/util/GProperties.groovy
new file mode 100644
index 0000000..500baf5
--- /dev/null
+++ b/src/main/groovy/groovy/util/GProperties.groovy
@@ -0,0 +1,204 @@
+/*
+ * 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.util
+
+import groovy.transform.CompileStatic
+
+import java.util.regex.Pattern
+
+/**
+ * Represents an enhanced properties, which supports interpolating in the values and importing other properties in classpath
+ *
+ * Usage:
+ * 1) Interpolating with {...}, e.g.
+ * <pre>
+ * # gproperties.properties
+ * groovy.greeting=Hello
+ * some.name=Daniel
+ * greeting.daniel={groovy.greeting},{some.name}
+ *
+ * // groovy script
+ * def gp = new GProperties()
+ * gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+ * assert 'Hello,Daniel' == gp.getProperty('greeting.daniel')
+ * </pre>
+ *
+ * 2) Importing with import.properties, e.g.
+ * <pre>
+ * # gproperties.properties
+ * import.properties=/groovy/util/gproperties_import.properties,/groovy/util/gproperties_import2.properties
+ * greeting.daniel={groovy.greeting},{some.name}
+ *
+ * # gproperties_import.properties
+ * groovy.greeting=Hello
+ *
+ * # gproperties_import2.properties
+ * some.name=Daniel
+ *
+ * // groovy script
+ * def gp = new GProperties()
+ * gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+ * assert 'Hello,Daniel' == gp.getProperty('greeting.daniel')
+ * </pre>
+ *
+ * 3) Evaluating with ${...}, e.g.
+ * <pre>
+ * # gproperties.properties
+ * greeting.daniel=Hello,Daniel in ${new java.text.SimpleDateFormat('yyyyMMdd').format(new Date())}
+ *
+ * // groovy script
+ * def gp = new GProperties(true) // Note: we should enable evaluating script manually
+ * gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+ * assert 'Hello,Daniel in 2018' == gp.getProperty('greeting.daniel') // Given running the script in 2018
+ * </pre>
+ *
+ * 4) Escaping with {{...}}, ${{...}}, e.g.
+ * <pre>
+ * # gproperties.properties
+ * greeting.daniel={{groovy.greeting}},{{some.name}} in ${{new java.text.SimpleDateFormat('yyyyMMdd').format(new Date())}}
+ *
+ * // groovy script
+ * def gp = new GProperties(true)
+ * gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+ * assert '{groovy.greeting},{some.name} in ${new java.text.SimpleDateFormat('yyyyMMdd').format(new Date())}' == gp.getProperty('greeting.daniel')
+ * </pre>
+ *
+ * @since 3.0.0
+ */
+@CompileStatic
+class GProperties extends Properties {
+ private static final long serialVersionUID = 6112578636029876860L
+ public static final String IMPORT_PROPERTIES_KEY = 'import.properties'
+ public static final String VAR_KEY = 'key'
+ public static final String VAR_PROPERTIES = 'properties'
+ private static final Pattern GROOVY_SCRIPT_PATTERN = Pattern.compile(/[$][{]\s*(.+?)\s*[}]/)
+ private static final Pattern INTERPOLATE_PATTERN = Pattern.compile(/[{]\s*(.+?)\s*[}]/)
+ private static final Pattern ESCAPE_PATTERN = Pattern.compile(/[{]([{].+?[}])[}]/)
+ private static final String LEFT_CURLY_BRACE = '{'
+ private static final String RIGHT_CURLY_BRACE = '}'
+ private static final String COMMA = ','
+ private final boolean evaluateScriptEnabled
+ private final GroovyShell groovyShell
+ private final List<GProperties> importPropertiesList = new ArrayList<>()
+
+ GProperties(boolean evaluateScriptEnabled=false, GroovyShell groovyShell=new GroovyShell()) {
+ this(null, evaluateScriptEnabled, groovyShell)
+ }
+
+ GProperties(Properties defaults, boolean evaluateScriptEnabled=false, GroovyShell groovyShell=new GroovyShell()) {
+ super(defaults)
+ this.evaluateScriptEnabled = evaluateScriptEnabled
+ this.groovyShell = groovyShell
+ }
+
+ @Override
+ String getProperty(String key) {
+ String value = super.getProperty(key)
+
+ if (!value) {
+ for (GProperties importProperties : importPropertiesList) {
+ value = importProperties.getProperty(key)
+
+ if (value) {
+ break
+ }
+ }
+ }
+
+ if (!value) {
+ return value
+ }
+
+ if (evaluateScriptEnabled) {
+ value = value.replaceAll(GROOVY_SCRIPT_PATTERN) { String _0, String _1 ->
+ if (_1.startsWith(LEFT_CURLY_BRACE) && _1.endsWith(RIGHT_CURLY_BRACE)) {
+ return _0
+ }
+ processImplicitVariables(key) {
+ groovyShell.evaluate(_1)
+ }
+ }
+ }
+
+ value = value.replaceAll(INTERPOLATE_PATTERN) { String _0, String _1 ->
+ if (_1.startsWith(LEFT_CURLY_BRACE) && _1.endsWith(RIGHT_CURLY_BRACE)) {
+ return _0
+ }
+
+ this.getProperty(_1) ?: _0
+ }
+
+ value.replaceAll(ESCAPE_PATTERN) { String _0, String _1 ->
+ _1
+ }
+ }
+
+ @Override
+ synchronized void load(Reader reader) throws IOException {
+ reader.withReader { it ->
+ super.load(it)
+ importProperties()
+ }
+ }
+
+ @Override
+ synchronized void load(InputStream inStream) throws IOException {
+ inStream.withStream {
+ super.load(it)
+ importProperties()
+ }
+ }
+
+ private void importProperties() {
+ String importPropertiesPaths = super.getProperty(IMPORT_PROPERTIES_KEY)
+
+ if (!importPropertiesPaths?.trim()) {
+ return
+ }
+
+ importPropertiesPaths.split(COMMA).collect { it.trim() }.each { String importPropertiesPath ->
+ if (!importPropertiesPath) {
+ return
+ }
+
+ GProperties importProperties = new GProperties()
+ def inputstream = GProperties.getResourceAsStream(importPropertiesPath)
+
+ if (!inputstream) {
+ throw new IOException("${importPropertiesPath} does not exist")
+ }
+
+ inputstream.withStream {
+ importProperties.load(it)
+ }
+
+ importPropertiesList << importProperties
+ }
+ }
+
+ private synchronized Object processImplicitVariables(String key, Closure c) {
+ groovyShell.setVariable(VAR_KEY, key)
+ groovyShell.setVariable(VAR_PROPERTIES, this)
+ def v = c()
+ groovyShell.removeVariable(VAR_PROPERTIES)
+ groovyShell.removeVariable(VAR_KEY)
+
+ return v
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/test-resources/groovy/util/gproperties.properties
----------------------------------------------------------------------
diff --git a/src/test-resources/groovy/util/gproperties.properties b/src/test-resources/groovy/util/gproperties.properties
new file mode 100644
index 0000000..e4f488d
--- /dev/null
+++ b/src/test-resources/groovy/util/gproperties.properties
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+import.properties=/groovy/util/gproperties_import.properties,/groovy/util/gproperties_import3.properties
+groovy.greeting={greeting.word},{some.name}
+groovy.greeting.with.smile={groovy.greeting} :)
+groovy.greeting.with.time={groovy.greeting.with.smile} in ${new java.text.SimpleDateFormat('yyyyMMdd').format(new Date())}
+groovy.greeting.with.missing=Hello,{none} {0}
+groovy.greeting.with.script=Hello,${properties.getProperty('some.name')}
+groovy.greeting.with.key=Hello,${key}
+groovy.greeting.with.escapes=Hello,{{some.name}}
+groovy.greeting.with.escapes2=Hello,${{properties.getProperty('some.name')}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/test-resources/groovy/util/gproperties_import.properties
----------------------------------------------------------------------
diff --git a/src/test-resources/groovy/util/gproperties_import.properties b/src/test-resources/groovy/util/gproperties_import.properties
new file mode 100644
index 0000000..7016e97
--- /dev/null
+++ b/src/test-resources/groovy/util/gproperties_import.properties
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+import.properties=/groovy/util/gproperties_import2.properties
+some.name=Daniel
+greeting.daniel={greeting.word},{some.name}
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/test-resources/groovy/util/gproperties_import2.properties
----------------------------------------------------------------------
diff --git a/src/test-resources/groovy/util/gproperties_import2.properties b/src/test-resources/groovy/util/gproperties_import2.properties
new file mode 100644
index 0000000..7d41c9f
--- /dev/null
+++ b/src/test-resources/groovy/util/gproperties_import2.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+greeting.word=H${'ell'}o
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/test-resources/groovy/util/gproperties_import3.properties
----------------------------------------------------------------------
diff --git a/src/test-resources/groovy/util/gproperties_import3.properties b/src/test-resources/groovy/util/gproperties_import3.properties
new file mode 100644
index 0000000..616c9a4
--- /dev/null
+++ b/src/test-resources/groovy/util/gproperties_import3.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+greeting.word2=Hi
http://git-wip-us.apache.org/repos/asf/groovy/blob/5de34ce5/src/test/groovy/util/GPropertiesTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/util/GPropertiesTest.groovy b/src/test/groovy/util/GPropertiesTest.groovy
new file mode 100644
index 0000000..fe9ae0d
--- /dev/null
+++ b/src/test/groovy/util/GPropertiesTest.groovy
@@ -0,0 +1,101 @@
+/*
+ * 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.util
+
+class GPropertiesTest extends GroovyTestCase {
+ void testImportProperties() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert '/groovy/util/gproperties_import.properties,/groovy/util/gproperties_import3.properties' == gp.getProperty('import.properties')
+ assert 'Daniel' == gp.getProperty('some.name')
+ assert 'Hello' == gp.getProperty('greeting.word')
+ assert 'Hi' == gp.getProperty('greeting.word2')
+ }
+
+ void testInterpolate() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert 'Hello,Daniel' == gp.getProperty('groovy.greeting')
+ }
+
+ void testInterpolate2() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert 'Hello,Daniel :)' == gp.getProperty('groovy.greeting.with.smile')
+ }
+
+ void testInterpolate3() {
+ def gp = new GProperties()
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert 'Hello,{none} {0}' == gp.getProperty('groovy.greeting.with.missing')
+ }
+
+ void testInterpolate4() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert 'Hello,Daniel' == gp.getProperty('greeting.daniel')
+ }
+
+ void testInterpolate5() {
+ def gp = new GProperties()
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert '''H${'ell'}o,Daniel''' == gp.getProperty('greeting.daniel')
+ }
+
+ void testEscape() {
+ def gp = new GProperties()
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert 'Hello,{some.name}' == gp.getProperty('groovy.greeting.with.escapes')
+ }
+
+ void testEscape2() {
+ def gp = new GProperties()
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert '''Hello,${properties.getProperty('some.name')}''' == gp.getProperty('groovy.greeting.with.escapes2')
+ }
+
+ void testInterpolateGroovyScript() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert "Hello,Daniel :) in ${new java.text.SimpleDateFormat('yyyyMMdd').format(new Date())}" == gp.getProperty('groovy.greeting.with.time')
+ }
+
+ void testInterpolateGroovyScript2() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert "Hello,Daniel" == gp.getProperty('groovy.greeting.with.script')
+ }
+
+ void testInterpolateGroovyScript3() {
+ def gp = new GProperties(true)
+ gp.load(GPropertiesTest.getResourceAsStream('/groovy/util/gproperties.properties'))
+
+ assert "Hello,groovy.greeting.with.key" == gp.getProperty('groovy.greeting.with.key')
+ }
+}