[ https://issues.apache.org/jira/browse/MNG-7820?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17760893#comment-17760893 ]
ASF GitHub Bot commented on MNG-7820: ------------------------------------- elharo commented on code in PR #1208: URL: https://github.com/apache/maven/pull/1208#discussion_r1311520008 ########## maven-core/src/main/java/org/apache/maven/utils/dag/DAG.java: ########## @@ -0,0 +1,240 @@ +/* + * 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 org.apache.maven.utils.dag; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * DAG = Directed Acyclic Graph + * + * @author <a href="michal.mac...@dimatics.com">Michal Maczka</a> + * + * TODO this class should be renamed from DAG to Dag Review Comment: Now is the time to do this ########## maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java: ########## @@ -103,6 +102,10 @@ public class DefaultProjectBuilder implements ProjectBuilder { private final RootLocator rootLocator; + public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US); Review Comment: private ########## maven-model-builder/src/main/java/org/apache/maven/utils/Os.java: ########## @@ -0,0 +1,383 @@ +/* Review Comment: This can't be copied over as is. It needs to be cleaned up and rethought. It's horribly out of date. ########## maven-core/src/main/java/org/apache/maven/utils/dag/CycleDetectedException.java: ########## @@ -0,0 +1,40 @@ +/* + * 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 org.apache.maven.utils.dag; Review Comment: This DAG stuff should probably be a separate PR from random StringUtils and IO fixes. ########## maven-model/src/main/java/org/apache/maven/model/io/xpp3/MavenXpp3ReaderEx.java: ########## @@ -112,13 +119,13 @@ public Model read(InputStream in, InputSource source) throws IOException, XmlPul * * @param parser a parser object. * @param strict a strict object. - * @throws IOException IOException if any. - * @throws XmlPullParserException XmlPullParserException if - * any. * @return Model + * @throws IOException IOException if any. Review Comment: no period, and in general "if any" is not sufficient description. Something like "if an I/O error occurs while reading from the underlying source" ########## maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuilder.java: ########## @@ -103,6 +102,10 @@ public class DefaultProjectBuilder implements ProjectBuilder { private final RootLocator rootLocator; + public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US); + + public static final String FAMILY_WINDOWS = "windows"; Review Comment: private In general, avoid introducing new public API ########## maven-core/src/main/java/org/apache/maven/properties/internal/EnvironmentUtils.java: ########## @@ -36,6 +34,10 @@ public class EnvironmentUtils { private static Properties envVars; + public static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.US); Review Comment: private ########## maven-core/src/main/java/org/apache/maven/utils/introspection/ReflectionValueExtractor.java: ########## @@ -0,0 +1,325 @@ +/* + * 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 org.apache.maven.utils.introspection; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * <p> + * Using simple dotted expressions to extract the values from an Object instance, For example we might want to extract a + * value like: <code>project.build.sourceDirectory</code> + * </p> + * <p> + * The implementation supports indexed, nested and mapped properties similar to the JSP way. + * </p> + * + * @author <a href="mailto:ja...@maven.org">Jason van Zyl </a> + * @author <a href="mailto:vincent.sive...@gmail.com">Vincent Siveton</a> + * + * @see <a href= + * "http://struts.apache.org/1.x/struts-taglib/indexedprops.html">http://struts.apache.org/1.x/struts-taglib/indexedprops.html</a> + */ +public class ReflectionValueExtractor { + private static final Class<?>[] CLASS_ARGS = new Class[0]; + + private static final Object[] OBJECT_ARGS = new Object[0]; + + /** + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen + * space overflows due to retention of discarded classloaders. + */ + private static final Map<Class<?>, WeakReference<ClassMap>> CLASS_MAPS = new WeakHashMap<>(); + + static final int EOF = -1; + + static final char PROPERTY_START = '.'; + + static final char INDEXED_START = '['; + + static final char INDEXED_END = ']'; + + static final char MAPPED_START = '('; + + static final char MAPPED_END = ')'; + + static class Tokenizer { + final String expression; + + int idx; + + Tokenizer(String expression) { + this.expression = expression; + } + + public int peekChar() { + return idx < expression.length() ? expression.charAt(idx) : EOF; + } + + public int skipChar() { + return idx < expression.length() ? expression.charAt(idx++) : EOF; + } + + public String nextToken(char delimiter) { + int start = idx; + + while (idx < expression.length() && delimiter != expression.charAt(idx)) { + idx++; + } + + // delimiter MUST be present + if (idx <= start || idx >= expression.length()) { + return null; + } + + return expression.substring(start, idx++); + } + + public String nextPropertyName() { + final int start = idx; + + while (idx < expression.length() && Character.isJavaIdentifierPart(expression.charAt(idx))) { + idx++; + } + + // property name does not require delimiter + if (idx <= start || idx > expression.length()) { + return null; + } + + return expression.substring(start, idx); + } + + public int getPosition() { + return idx < expression.length() ? idx : EOF; + } + + // to make tokenizer look pretty in debugger + @Override + public String toString() { + return idx < expression.length() ? expression.substring(idx) : "<EOF>"; + } + } + + private ReflectionValueExtractor() {} + + /** + * <p>The implementation supports indexed, nested and mapped properties.</p> + * + * <ul> + * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li> + * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code> + * pattern, i.e. "user.addresses[1].street"</li> + * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e. + * "user.addresses(myAddress).street"</li> + * </ul> + * + * @param expression not null expression + * @param root not null object + * @return the object defined by the expression + * @throws Exception if any + */ + public static Object evaluate(String expression, Object root) throws Exception { + return evaluate(expression, root, true); + } + + /** + * <p>The implementation supports indexed, nested and mapped properties.</p> + * + * <ul> + * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li> + * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code> + * pattern, i.e. "user.addresses[1].street"</li> + * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e. + * "user.addresses(myAddress).street"</li> + * </ul> + * + * @param expression not null expression + * @param root not null object + * @param trimRootToken root start + * @return the object defined by the expression + * @throws Exception if any + */ + // TODO: don't throw Exception + public static Object evaluate(String expression, final Object root, final boolean trimRootToken) throws Exception { + Object value = root; + + // ---------------------------------------------------------------------- + // Walk the dots and retrieve the ultimate value desired from the + // MavenProject instance. + // ---------------------------------------------------------------------- + + if (expression == null || expression.isEmpty() || !Character.isJavaIdentifierStart(expression.charAt(0))) { + return null; + } + + boolean hasDots = expression.indexOf(PROPERTY_START) >= 0; + + final Tokenizer tokenizer; + if (trimRootToken && hasDots) { + tokenizer = new Tokenizer(expression); + tokenizer.nextPropertyName(); + if (tokenizer.getPosition() == EOF) { + return null; + } + } else { + tokenizer = new Tokenizer("." + expression); + } + + int propertyPosition = tokenizer.getPosition(); + while (value != null && tokenizer.peekChar() != EOF) { + switch (tokenizer.skipChar()) { + case INDEXED_START: + value = getIndexedValue( + expression, + propertyPosition, + tokenizer.getPosition(), + value, + tokenizer.nextToken(INDEXED_END)); + break; + case MAPPED_START: + value = getMappedValue( + expression, + propertyPosition, + tokenizer.getPosition(), + value, + tokenizer.nextToken(MAPPED_END)); + break; + case PROPERTY_START: + propertyPosition = tokenizer.getPosition(); + value = getPropertyValue(value, tokenizer.nextPropertyName()); + break; + default: + // could not parse expression + return null; + } + } + + return value; + } + + private static Object getMappedValue( + final String expression, final int from, final int to, final Object value, final String key) + throws Exception { + if (value == null || key == null) { + return null; + } + + if (value instanceof Map) { + Object[] localParams = new Object[] {key}; + ClassMap classMap = getClassMap(value.getClass()); + Method method = classMap.findMethod("get", localParams); + return method.invoke(value, localParams); + } + + final String message = String.format( + "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", + expression.subSequence(from, to), from, value.getClass()); + + throw new Exception(message); Review Comment: This should be a more specific exception. Note: I do not care if this was already done incorrectly somewhere else. It's still wrong here. ########## maven-model/src/main/java/org/apache/maven/model/io/xpp3/MavenXpp3ReaderEx.java: ########## @@ -54,56 +55,62 @@ public boolean getAddDefaultEntities() { /** * @param reader a reader object. * @param strict a strict object. - * @throws IOException IOException if any. - * @throws XmlPullParserException XmlPullParserException if - * any. * @return Model + * @throws IOException IOException if any. + * @throws XMLStreamException XMLStreamException if + * any. */ - public Model read(Reader reader, boolean strict, InputSource source) throws IOException, XmlPullParserException { - XmlPullParser parser = - addDefaultEntities ? new MXParser(EntityReplacementMap.defaultEntityReplacementMap) : new MXParser(); - parser.setInput(reader); + public Model read(Reader reader, boolean strict, InputSource source) throws IOException, XMLStreamException { + XMLInputFactory factory = new com.ctc.wstx.stax.WstxInputFactory(); + factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false); + XMLStreamReader parser = null; Review Comment: try block needs to surround everything ########## maven-core/src/main/java/org/apache/maven/utils/introspection/ReflectionValueExtractor.java: ########## @@ -0,0 +1,325 @@ +/* + * 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 org.apache.maven.utils.introspection; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * <p> + * Using simple dotted expressions to extract the values from an Object instance, For example we might want to extract a + * value like: <code>project.build.sourceDirectory</code> + * </p> + * <p> + * The implementation supports indexed, nested and mapped properties similar to the JSP way. + * </p> + * + * @author <a href="mailto:ja...@maven.org">Jason van Zyl </a> + * @author <a href="mailto:vincent.sive...@gmail.com">Vincent Siveton</a> + * + * @see <a href= + * "http://struts.apache.org/1.x/struts-taglib/indexedprops.html">http://struts.apache.org/1.x/struts-taglib/indexedprops.html</a> + */ +public class ReflectionValueExtractor { + private static final Class<?>[] CLASS_ARGS = new Class[0]; + + private static final Object[] OBJECT_ARGS = new Object[0]; + + /** + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen + * space overflows due to retention of discarded classloaders. + */ + private static final Map<Class<?>, WeakReference<ClassMap>> CLASS_MAPS = new WeakHashMap<>(); + + static final int EOF = -1; + + static final char PROPERTY_START = '.'; + + static final char INDEXED_START = '['; + + static final char INDEXED_END = ']'; + + static final char MAPPED_START = '('; + + static final char MAPPED_END = ')'; + + static class Tokenizer { + final String expression; + + int idx; + + Tokenizer(String expression) { + this.expression = expression; + } + + public int peekChar() { + return idx < expression.length() ? expression.charAt(idx) : EOF; + } + + public int skipChar() { + return idx < expression.length() ? expression.charAt(idx++) : EOF; + } + + public String nextToken(char delimiter) { + int start = idx; + + while (idx < expression.length() && delimiter != expression.charAt(idx)) { + idx++; + } + + // delimiter MUST be present + if (idx <= start || idx >= expression.length()) { + return null; + } + + return expression.substring(start, idx++); + } + + public String nextPropertyName() { + final int start = idx; + + while (idx < expression.length() && Character.isJavaIdentifierPart(expression.charAt(idx))) { + idx++; + } + + // property name does not require delimiter + if (idx <= start || idx > expression.length()) { + return null; + } + + return expression.substring(start, idx); + } + + public int getPosition() { + return idx < expression.length() ? idx : EOF; + } + + // to make tokenizer look pretty in debugger + @Override + public String toString() { + return idx < expression.length() ? expression.substring(idx) : "<EOF>"; + } + } + + private ReflectionValueExtractor() {} + + /** + * <p>The implementation supports indexed, nested and mapped properties.</p> + * + * <ul> + * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li> + * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code> + * pattern, i.e. "user.addresses[1].street"</li> + * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e. + * "user.addresses(myAddress).street"</li> + * </ul> + * + * @param expression not null expression + * @param root not null object + * @return the object defined by the expression + * @throws Exception if any + */ + public static Object evaluate(String expression, Object root) throws Exception { + return evaluate(expression, root, true); + } + + /** + * <p>The implementation supports indexed, nested and mapped properties.</p> + * + * <ul> + * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li> + * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code> + * pattern, i.e. "user.addresses[1].street"</li> + * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e. + * "user.addresses(myAddress).street"</li> + * </ul> + * + * @param expression not null expression + * @param root not null object + * @param trimRootToken root start + * @return the object defined by the expression + * @throws Exception if any + */ + // TODO: don't throw Exception + public static Object evaluate(String expression, final Object root, final boolean trimRootToken) throws Exception { + Object value = root; + + // ---------------------------------------------------------------------- + // Walk the dots and retrieve the ultimate value desired from the + // MavenProject instance. + // ---------------------------------------------------------------------- + + if (expression == null || expression.isEmpty() || !Character.isJavaIdentifierStart(expression.charAt(0))) { + return null; + } + + boolean hasDots = expression.indexOf(PROPERTY_START) >= 0; + + final Tokenizer tokenizer; + if (trimRootToken && hasDots) { + tokenizer = new Tokenizer(expression); + tokenizer.nextPropertyName(); + if (tokenizer.getPosition() == EOF) { + return null; + } + } else { + tokenizer = new Tokenizer("." + expression); + } + + int propertyPosition = tokenizer.getPosition(); + while (value != null && tokenizer.peekChar() != EOF) { + switch (tokenizer.skipChar()) { + case INDEXED_START: + value = getIndexedValue( + expression, + propertyPosition, + tokenizer.getPosition(), + value, + tokenizer.nextToken(INDEXED_END)); + break; + case MAPPED_START: + value = getMappedValue( + expression, + propertyPosition, + tokenizer.getPosition(), + value, + tokenizer.nextToken(MAPPED_END)); + break; + case PROPERTY_START: + propertyPosition = tokenizer.getPosition(); + value = getPropertyValue(value, tokenizer.nextPropertyName()); + break; + default: + // could not parse expression + return null; + } + } + + return value; + } + + private static Object getMappedValue( + final String expression, final int from, final int to, final Object value, final String key) + throws Exception { + if (value == null || key == null) { + return null; + } + + if (value instanceof Map) { + Object[] localParams = new Object[] {key}; + ClassMap classMap = getClassMap(value.getClass()); + Method method = classMap.findMethod("get", localParams); + return method.invoke(value, localParams); + } + + final String message = String.format( + "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", + expression.subSequence(from, to), from, value.getClass()); + + throw new Exception(message); + } + + private static Object getIndexedValue( + final String expression, final int from, final int to, final Object value, final String indexStr) + throws Exception { + try { + int index = Integer.parseInt(indexStr); Review Comment: indexStr should probably be an int ########## maven-core/src/main/java/org/apache/maven/utils/introspection/ReflectionValueExtractor.java: ########## @@ -0,0 +1,325 @@ +/* + * 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 org.apache.maven.utils.introspection; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * <p> + * Using simple dotted expressions to extract the values from an Object instance, For example we might want to extract a + * value like: <code>project.build.sourceDirectory</code> + * </p> + * <p> + * The implementation supports indexed, nested and mapped properties similar to the JSP way. + * </p> + * + * @author <a href="mailto:ja...@maven.org">Jason van Zyl </a> + * @author <a href="mailto:vincent.sive...@gmail.com">Vincent Siveton</a> + * + * @see <a href= + * "http://struts.apache.org/1.x/struts-taglib/indexedprops.html">http://struts.apache.org/1.x/struts-taglib/indexedprops.html</a> + */ +public class ReflectionValueExtractor { + private static final Class<?>[] CLASS_ARGS = new Class[0]; + + private static final Object[] OBJECT_ARGS = new Object[0]; + + /** + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen + * space overflows due to retention of discarded classloaders. + */ + private static final Map<Class<?>, WeakReference<ClassMap>> CLASS_MAPS = new WeakHashMap<>(); + + static final int EOF = -1; + + static final char PROPERTY_START = '.'; + + static final char INDEXED_START = '['; + + static final char INDEXED_END = ']'; + + static final char MAPPED_START = '('; + + static final char MAPPED_END = ')'; + + static class Tokenizer { + final String expression; + + int idx; + + Tokenizer(String expression) { + this.expression = expression; + } + + public int peekChar() { + return idx < expression.length() ? expression.charAt(idx) : EOF; + } + + public int skipChar() { + return idx < expression.length() ? expression.charAt(idx++) : EOF; + } + + public String nextToken(char delimiter) { + int start = idx; + + while (idx < expression.length() && delimiter != expression.charAt(idx)) { + idx++; + } + + // delimiter MUST be present + if (idx <= start || idx >= expression.length()) { + return null; + } + + return expression.substring(start, idx++); + } + + public String nextPropertyName() { + final int start = idx; + + while (idx < expression.length() && Character.isJavaIdentifierPart(expression.charAt(idx))) { + idx++; + } + + // property name does not require delimiter + if (idx <= start || idx > expression.length()) { + return null; + } + + return expression.substring(start, idx); + } + + public int getPosition() { + return idx < expression.length() ? idx : EOF; + } + + // to make tokenizer look pretty in debugger + @Override + public String toString() { + return idx < expression.length() ? expression.substring(idx) : "<EOF>"; + } + } + + private ReflectionValueExtractor() {} + + /** + * <p>The implementation supports indexed, nested and mapped properties.</p> + * + * <ul> + * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li> + * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code> + * pattern, i.e. "user.addresses[1].street"</li> + * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e. + * "user.addresses(myAddress).street"</li> + * </ul> + * + * @param expression not null expression + * @param root not null object + * @return the object defined by the expression + * @throws Exception if any + */ + public static Object evaluate(String expression, Object root) throws Exception { + return evaluate(expression, root, true); + } + + /** + * <p>The implementation supports indexed, nested and mapped properties.</p> + * + * <ul> + * <li>nested properties should be defined by a dot, i.e. "user.address.street"</li> + * <li>indexed properties (java.util.List or array instance) should be contains <code>(\\w+)\\[(\\d+)\\]</code> + * pattern, i.e. "user.addresses[1].street"</li> + * <li>mapped properties should be contains <code>(\\w+)\\((.+)\\)</code> pattern, i.e. + * "user.addresses(myAddress).street"</li> + * </ul> + * + * @param expression not null expression + * @param root not null object + * @param trimRootToken root start + * @return the object defined by the expression + * @throws Exception if any + */ + // TODO: don't throw Exception + public static Object evaluate(String expression, final Object root, final boolean trimRootToken) throws Exception { + Object value = root; + + // ---------------------------------------------------------------------- + // Walk the dots and retrieve the ultimate value desired from the + // MavenProject instance. + // ---------------------------------------------------------------------- + + if (expression == null || expression.isEmpty() || !Character.isJavaIdentifierStart(expression.charAt(0))) { + return null; + } + + boolean hasDots = expression.indexOf(PROPERTY_START) >= 0; + + final Tokenizer tokenizer; + if (trimRootToken && hasDots) { + tokenizer = new Tokenizer(expression); + tokenizer.nextPropertyName(); + if (tokenizer.getPosition() == EOF) { + return null; + } + } else { + tokenizer = new Tokenizer("." + expression); + } + + int propertyPosition = tokenizer.getPosition(); + while (value != null && tokenizer.peekChar() != EOF) { + switch (tokenizer.skipChar()) { + case INDEXED_START: + value = getIndexedValue( + expression, + propertyPosition, + tokenizer.getPosition(), + value, + tokenizer.nextToken(INDEXED_END)); + break; + case MAPPED_START: + value = getMappedValue( + expression, + propertyPosition, + tokenizer.getPosition(), + value, + tokenizer.nextToken(MAPPED_END)); + break; + case PROPERTY_START: + propertyPosition = tokenizer.getPosition(); + value = getPropertyValue(value, tokenizer.nextPropertyName()); + break; + default: + // could not parse expression + return null; + } + } + + return value; + } + + private static Object getMappedValue( + final String expression, final int from, final int to, final Object value, final String key) + throws Exception { + if (value == null || key == null) { + return null; + } + + if (value instanceof Map) { + Object[] localParams = new Object[] {key}; + ClassMap classMap = getClassMap(value.getClass()); + Method method = classMap.findMethod("get", localParams); + return method.invoke(value, localParams); + } + + final String message = String.format( + "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", + expression.subSequence(from, to), from, value.getClass()); + + throw new Exception(message); + } + + private static Object getIndexedValue( + final String expression, final int from, final int to, final Object value, final String indexStr) + throws Exception { + try { + int index = Integer.parseInt(indexStr); + + if (value.getClass().isArray()) { + return Array.get(value, index); + } + + if (value instanceof List) { + ClassMap classMap = getClassMap(value.getClass()); + // use get method on List interface + Object[] localParams = new Object[] {index}; + Method method = classMap.findMethod("get", localParams); + return method.invoke(value, localParams); + } + } catch (NumberFormatException e) { + return null; Review Comment: This seems very suspicious ########## maven-model/src/main/java/org/apache/maven/model/io/xpp3/MavenXpp3ReaderEx.java: ########## @@ -112,13 +119,13 @@ public Model read(InputStream in, InputSource source) throws IOException, XmlPul * * @param parser a parser object. * @param strict a strict object. - * @throws IOException IOException if any. - * @throws XmlPullParserException XmlPullParserException if - * any. * @return Model + * @throws IOException IOException if any. + * @throws XMLStreamException XMLStreamException if Review Comment: definitely needs to be expanded and distinguished from IOException > Remove dependency on plexus-utils > --------------------------------- > > Key: MNG-7820 > URL: https://issues.apache.org/jira/browse/MNG-7820 > Project: Maven > Issue Type: Task > Reporter: Guillaume Nodet > Priority: Major > Fix For: 4.0.0-alpha-8 > > -- This message was sent by Atlassian Jira (v8.20.10#820010)