This is an automated email from the ASF dual-hosted git repository. martin_s pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/archiva-redback-components-expression-evaluator.git
commit ae76ae6c11bb8328c6803fb4eb7b7aed95a97d74 Author: Olivier Lamy <ol...@apache.org> AuthorDate: Tue Apr 17 21:47:27 2012 +0000 remove plexus naming prefix git-svn-id: https://svn.apache.org/repos/asf/archiva/redback/redback-components/trunk@1327299 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 56 ++++++ .../evaluator/DefaultExpressionEvaluator.java | 162 ++++++++++++++++ .../plexus/evaluator/EvaluatorException.java | 50 +++++ .../plexus/evaluator/ExpressionEvaluator.java | 65 +++++++ .../plexus/evaluator/ExpressionSource.java | 37 ++++ .../sources/PropertiesExpressionSource.java | 67 +++++++ .../sources/SystemPropertyExpressionSource.java | 47 +++++ .../sources/DefaultExpressionEvaluatorTest.java | 212 +++++++++++++++++++++ 8 files changed, 696 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3d16462 --- /dev/null +++ b/pom.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.archiva.redback.components</groupId> + <artifactId>redback-components</artifactId> + <version>2.0-SNAPSHOT</version> + <relativePath>../redback-components-parent/pom.xml</relativePath> + </parent> + <artifactId>plexus-expression-evaluator</artifactId> + <version>2.0-SNAPSHOT</version> + <name>Plexus Expression Evaluator Components</name> + <packaging>jar</packaging> + + <url>http://archiva.apache.org/redback/components/${project.artifactId}</url> + + <distributionManagement> + <site> + <id>apache.website</id> + <url>scp://people.apache.org/www/archiva.apache.org/redback/components/${project.artifactId}</url> + </site> + </distributionManagement> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/archiva/redback/redback-components/trunk/plexus-expression-evaluator</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/archiva/redback/redback-components/trunk/plexus-expression-evaluator</developerConnection> + <url>http://svn.apache.org/viewvc/archiva/redback/redback-components/trunk/plexus-expression-evaluator</url> + </scm> + + <dependencies> + <dependency> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-utils</artifactId> + <version>1.5.6</version> + </dependency> + </dependencies> + +</project> diff --git a/src/main/java/org/codehaus/plexus/evaluator/DefaultExpressionEvaluator.java b/src/main/java/org/codehaus/plexus/evaluator/DefaultExpressionEvaluator.java new file mode 100644 index 0000000..ad3a5e5 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/evaluator/DefaultExpressionEvaluator.java @@ -0,0 +1,162 @@ +package org.codehaus.plexus.evaluator; + +/* + * 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 org.codehaus.plexus.util.StringUtils; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * DefaultExpressionEvaluator + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + * @plexus.component role="org.codehaus.plexus.evaluator.ExpressionEvaluator" + * role-hint="default" + * instantiation-strategy="per-lookup" + */ +public class DefaultExpressionEvaluator + implements ExpressionEvaluator +{ + private List expressionSources; + + public DefaultExpressionEvaluator() + { + expressionSources = new ArrayList(); + } + + public void addExpressionSource( ExpressionSource source ) + { + expressionSources.add( source ); + } + + public String expand( String str ) + throws EvaluatorException + { + return recursiveExpand( str, new ArrayList() ); + } + + private String recursiveExpand( String str, List seenExpressions ) + throws EvaluatorException + { + if ( StringUtils.isEmpty( str ) ) + { + // Empty string. Fail fast. + return str; + } + + if ( str.indexOf( "${" ) < 0 ) + { + // Contains no potential expressions. Fail fast. + return str; + } + + if ( this.expressionSources.isEmpty() ) + { + throw new EvaluatorException( "Unable to expand expressions with empty ExpressionSource list." ); + } + + Pattern pat = Pattern.compile( "(?<=[^$]|^)(\\$\\{[^}]*\\})" ); + Matcher mat = pat.matcher( str ); + int offset = 0; + String expression; + String value; + StringBuffer expanded = new StringBuffer(); + + while ( mat.find( offset ) ) + { + expression = mat.group( 1 ); + + if ( seenExpressions.contains( expression ) ) + { + throw new EvaluatorException( "A recursive cycle has been detected with expression " + expression + "." ); + } + + seenExpressions.add( expression ); + + expanded.append( str.substring( offset, mat.start( 1 ) ) ); + value = findValue( expression ); + if ( value != null ) + { + String resolvedValue = recursiveExpand( value, seenExpressions ); + expanded.append( resolvedValue ); + } + else + { + expanded.append( expression ); + } + offset = mat.end( 1 ); + } + + expanded.append( str.substring( offset ) ); + + if ( expanded.indexOf( "$$" ) >= 0 ) + { + // Special case for escaped content. + return expanded.toString().replaceAll( "\\$\\$", "\\$" ); + } + else + { + // return expanded + return expanded.toString(); + } + } + + private String findValue( String expression ) + { + String newExpression = expression.trim(); + if ( newExpression.startsWith( "${" ) && newExpression.endsWith( "}" ) ) + { + newExpression = newExpression.substring( 2, newExpression.length() - 1 ); + } + + if ( StringUtils.isEmpty( newExpression ) ) + { + return null; + } + + String value = null; + Iterator it = this.expressionSources.iterator(); + while ( it.hasNext() ) + { + ExpressionSource source = (ExpressionSource) it.next(); + value = source.getExpressionValue( newExpression ); + if ( value != null ) + { + return value; + } + } + return null; + } + + public List getExpressionSourceList() + { + return this.expressionSources; + } + + public boolean removeExpressionSource( ExpressionSource source ) + { + return this.expressionSources.remove( source ); + } +} diff --git a/src/main/java/org/codehaus/plexus/evaluator/EvaluatorException.java b/src/main/java/org/codehaus/plexus/evaluator/EvaluatorException.java new file mode 100644 index 0000000..8cade66 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/evaluator/EvaluatorException.java @@ -0,0 +1,50 @@ +package org.codehaus.plexus.evaluator; + +/* + * 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. + */ + +/** + * EvaluatorException + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class EvaluatorException + extends Exception +{ + public EvaluatorException() + { + super(); + } + + public EvaluatorException( String message, Throwable cause ) + { + super( message, cause ); + } + + public EvaluatorException( String message ) + { + super( message ); + } + + public EvaluatorException( Throwable cause ) + { + super( cause ); + } +} diff --git a/src/main/java/org/codehaus/plexus/evaluator/ExpressionEvaluator.java b/src/main/java/org/codehaus/plexus/evaluator/ExpressionEvaluator.java new file mode 100644 index 0000000..5b38f72 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/evaluator/ExpressionEvaluator.java @@ -0,0 +1,65 @@ +package org.codehaus.plexus.evaluator; + +/* + * 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 java.util.List; + +/** + * ExpressionEvaluator + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public interface ExpressionEvaluator +{ + String ROLE = ExpressionEvaluator.class.getName(); + + /** + * Add a source for expression resolution. + * + * @param source the source to add. + */ + void addExpressionSource( ExpressionSource source ); + + /** + * Evaluate a string, and expand expressions as needed. + * + * @param str the expression + * @return the value of the expression + * @throws EvaluatorException if a problem occurs whilst evaluating + */ + String expand( String str ) + throws EvaluatorException; + + /** + * Get the List of expression sources. + * + * @return the list of expression sources. + */ + List getExpressionSourceList(); + + /** + * Remove a specific expression source. + * + * @param source the source to remove. + * @return true if expression source was removed. + */ + boolean removeExpressionSource( ExpressionSource source ); +} diff --git a/src/main/java/org/codehaus/plexus/evaluator/ExpressionSource.java b/src/main/java/org/codehaus/plexus/evaluator/ExpressionSource.java new file mode 100644 index 0000000..55a538e --- /dev/null +++ b/src/main/java/org/codehaus/plexus/evaluator/ExpressionSource.java @@ -0,0 +1,37 @@ +package org.codehaus.plexus.evaluator; + +/* + * 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. + */ + +/** + * ExpressionSource + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public interface ExpressionSource +{ + /** + * Gets a value for a provided Expression. + * + * @param expression the expression to attempt to get a value for. + * @return the value for the expression, or null if no value found. + */ + String getExpressionValue( String expression ); +} diff --git a/src/main/java/org/codehaus/plexus/evaluator/sources/PropertiesExpressionSource.java b/src/main/java/org/codehaus/plexus/evaluator/sources/PropertiesExpressionSource.java new file mode 100644 index 0000000..9cd1387 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/evaluator/sources/PropertiesExpressionSource.java @@ -0,0 +1,67 @@ +package org.codehaus.plexus.evaluator.sources; + +/* + * 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 org.codehaus.plexus.evaluator.ExpressionSource; + +import java.util.Properties; + +/** + * PropertiesExpressionSource + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + * + * @plexus.component role="org.codehaus.plexus.evaluator.ExpressionSource" + * role-hint="properties" + */ +public class PropertiesExpressionSource + implements ExpressionSource +{ + private Properties properties; + + public String getExpressionValue( String expression ) + { + if ( properties == null ) + { + throw new IllegalStateException( "Properties object has not been initialized." ); + } + + try + { + return properties.getProperty( expression ); + } + catch ( Exception e ) + { + return null; + } + } + + public Properties getProperties() + { + return properties; + } + + public void setProperties( Properties properties ) + { + this.properties = properties; + } + +} diff --git a/src/main/java/org/codehaus/plexus/evaluator/sources/SystemPropertyExpressionSource.java b/src/main/java/org/codehaus/plexus/evaluator/sources/SystemPropertyExpressionSource.java new file mode 100644 index 0000000..ddbba84 --- /dev/null +++ b/src/main/java/org/codehaus/plexus/evaluator/sources/SystemPropertyExpressionSource.java @@ -0,0 +1,47 @@ +package org.codehaus.plexus.evaluator.sources; + +/* + * 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 org.codehaus.plexus.evaluator.ExpressionSource; + +/** + * SystemPropertyExpressionSource + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + * + * @plexus.component role="org.codehaus.plexus.evaluator.ExpressionSource" + * role-hint="sysprops" + */ +public class SystemPropertyExpressionSource + implements ExpressionSource +{ + public String getExpressionValue( String expression ) + { + try + { + return System.getProperty( expression ); + } + catch ( Exception e ) + { + return null; + } + } +} diff --git a/src/test/java/org/codehaus/plexus/evaluator/sources/DefaultExpressionEvaluatorTest.java b/src/test/java/org/codehaus/plexus/evaluator/sources/DefaultExpressionEvaluatorTest.java new file mode 100644 index 0000000..c6d15e2 --- /dev/null +++ b/src/test/java/org/codehaus/plexus/evaluator/sources/DefaultExpressionEvaluatorTest.java @@ -0,0 +1,212 @@ +package org.codehaus.plexus.evaluator.sources; + +/* + * 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 junit.framework.TestCase; +import org.codehaus.plexus.evaluator.DefaultExpressionEvaluator; +import org.codehaus.plexus.evaluator.EvaluatorException; +import org.codehaus.plexus.evaluator.ExpressionEvaluator; + +import java.util.Properties; + +/** + * DefaultExpressionEvaluatorTest + * + * @author <a href="mailto:joa...@erdfelt.com">Joakim Erdfelt</a> + * @version $Id$ + */ +public class DefaultExpressionEvaluatorTest + extends TestCase +{ + private ExpressionEvaluator evaluator; + + protected void setUp() + throws Exception + { + super.setUp(); + + evaluator = new DefaultExpressionEvaluator(); + } + + public void testSimple() + throws EvaluatorException + { + Properties props = new Properties(); + props.setProperty( "fruit", "apple" ); + + PropertiesExpressionSource propsSource = new PropertiesExpressionSource(); + propsSource.setProperties( props ); + evaluator.addExpressionSource( propsSource ); + + String expression = "${fruit}"; + String expected = "apple"; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testSimpleStartOfLine() + throws EvaluatorException + { + Properties props = new Properties(); + props.setProperty( "fruit", "apple" ); + + PropertiesExpressionSource propsSource = new PropertiesExpressionSource(); + propsSource.setProperties( props ); + evaluator.addExpressionSource( propsSource ); + + String expression = "${fruit} is good for you."; + String expected = "apple is good for you."; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testSimpleEndOfLine() + throws EvaluatorException + { + Properties props = new Properties(); + props.setProperty( "fruit", "apple" ); + + PropertiesExpressionSource propsSource = new PropertiesExpressionSource(); + propsSource.setProperties( props ); + evaluator.addExpressionSource( propsSource ); + + String expression = "watch out for the worm in the ${fruit}"; + String expected = "watch out for the worm in the apple"; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testSimpleSystemProperty() + throws EvaluatorException + { + evaluator.addExpressionSource( new SystemPropertyExpressionSource() ); + + String userHome = System.getProperty( "user.home" ); + String expression = "My HOME directory is ${user.home}"; + String expected = "My HOME directory is " + userHome; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testMultiExpression() + throws EvaluatorException + { + evaluator.addExpressionSource( new SystemPropertyExpressionSource() ); + + String userName = System.getProperty( "user.name" ); + String userHome = System.getProperty( "user.home" ); + String expression = "${user.name}'s home directory is ${user.home}"; + String expected = userName + "'s home directory is " + userHome; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + /** + * This use case was discovered by a user of archiva. + * The last expression doesn't get evaluated properly. + * <p/> + * The result (with the bug) was "2.0.4${prj.ver.suf}" + */ + public void testMultiExpressionVersionBug() + throws EvaluatorException + { + Properties props = new Properties(); + props.setProperty( "prj.ver.maj", "2" ); + props.setProperty( "prj.ver.min", "0" ); + props.setProperty( "prj.ver.inc", "4" ); + props.setProperty( "prj.ver.suf", "-SNAPSHOT" ); + + PropertiesExpressionSource propsSource = new PropertiesExpressionSource(); + propsSource.setProperties( props ); + evaluator.addExpressionSource( propsSource ); + + String expression = "${prj.ver.maj}.${prj.ver.min}.${prj.ver.inc}${prj.ver.suf}"; + String expected = "2.0.4-SNAPSHOT"; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testEscaping() + throws EvaluatorException + { + evaluator.addExpressionSource( new SystemPropertyExpressionSource() ); + + String userName = System.getProperty( "user.name" ); + String userHome = System.getProperty( "user.home" ); + String expression = "${user.name}'s home directory is ${user.home} (fetched via $${user.home} expression)"; + String expected = userName + "'s home directory is " + userHome + " (fetched via ${user.home} expression)"; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testRecursiveSimple() + throws EvaluatorException + { + PropertiesExpressionSource propsource = new PropertiesExpressionSource(); + Properties props = new Properties(); + + // Create intentional recursive lookup. + props.setProperty( "main.dir", "${target.dir}/classes" ); + props.setProperty( "target.dir", "./target" ); + + propsource.setProperties( props ); + + evaluator.addExpressionSource( propsource ); + evaluator.addExpressionSource( new SystemPropertyExpressionSource() ); + + String expression = "My classes directory is ${main.dir}"; + String expected = "My classes directory is ./target/classes"; + + String actual = evaluator.expand( expression ); + assertEquals( expected, actual ); + } + + public void testRecursiveCycle() + { + PropertiesExpressionSource propsource = new PropertiesExpressionSource(); + Properties props = new Properties(); + + // Create intentional recursive lookup. + props.setProperty( "main.dir", "${test.dir}/target/classes" ); + props.setProperty( "test.dir", "${main.dir}/target/test-classes" ); + + propsource.setProperties( props ); + + evaluator.addExpressionSource( propsource ); + evaluator.addExpressionSource( new SystemPropertyExpressionSource() ); + + try + { + evaluator.expand( "My main dir is ${main.dir}" ); + fail( "Should have thrown an EvaluatorException due to recursive cycle." ); + } + catch ( EvaluatorException e ) + { + // Expected path. + } + } +} -- To stop receiving notification emails like this one, please contact marti...@apache.org.