blob: 59e542e7d00f5c9795b3745215128561a6d3a86a [file] [log] [blame]
/*
* 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.plugin.doap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.RDFReader;
import org.apache.jena.rdf.model.impl.RDFDefaultErrorHandler;
import org.apache.maven.model.Contributor;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Proxy;
import org.apache.maven.settings.Settings;
import org.apache.maven.wagon.proxy.ProxyInfo;
import org.apache.maven.wagon.proxy.ProxyUtils;
import org.codehaus.plexus.i18n.I18N;
import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
import org.codehaus.plexus.interpolation.InterpolationException;
import org.codehaus.plexus.interpolation.ObjectBasedValueSource;
import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.introspection.ClassMap;
import org.codehaus.plexus.util.xml.XMLWriter;
import org.codehaus.plexus.util.xml.XmlWriterUtil;
/**
* Utility class for {@link DoapMojo} class.
*
* @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
* @since 1.0
*/
public class DoapUtil {
/** Email regex */
private static final String EMAIL_REGEX =
"^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
/** Email pattern */
private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX);
/** Magic number to repeat '=' */
private static final int REPEAT_EQUALS = 21;
/** The default timeout used when fetching url, i.e. 2000. */
public static final int DEFAULT_TIMEOUT = 2000;
/** RDF resource attribute */
protected static final String RDF_RESOURCE = "rdf:resource";
/** RDF nodeID attribute */
protected static final String RDF_NODE_ID = "rdf:nodeID";
/** DoaP Organizations stored by name */
private static Map<String, DoapUtil.Organization> organizations = new HashMap<>();
/**
* Write comments in the DOAP file header
*
* @param writer not null
*/
public static void writeHeader(XMLWriter writer) {
XmlWriterUtil.writeLineBreak(writer);
XmlWriterUtil.writeCommentLineBreak(writer);
XmlWriterUtil.writeComment(
writer,
StringUtils.repeat("=", REPEAT_EQUALS) + " - DO NOT EDIT THIS FILE! - "
+ StringUtils.repeat("=", REPEAT_EQUALS));
XmlWriterUtil.writeCommentLineBreak(writer);
XmlWriterUtil.writeComment(writer, " ");
XmlWriterUtil.writeComment(writer, "Any modifications will be overwritten.");
XmlWriterUtil.writeComment(writer, " ");
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.US);
XmlWriterUtil.writeComment(
writer,
"Generated by Maven Doap Plugin " + getPluginVersion() + " on "
+ dateFormat.format(new Date(System.currentTimeMillis())));
XmlWriterUtil.writeComment(writer, "See: http://maven.apache.org/plugins/maven-doap-plugin/");
XmlWriterUtil.writeComment(writer, " ");
XmlWriterUtil.writeCommentLineBreak(writer);
XmlWriterUtil.writeLineBreak(writer);
}
/**
* Write comment.
*
* @param writer not null
* @param comment not null
* @throws IllegalArgumentException if comment is null or empty
* @since 1.1
*/
public static void writeComment(XMLWriter writer, String comment) throws IllegalArgumentException {
if (StringUtils.isEmpty(comment)) {
throw new IllegalArgumentException("comment should be defined");
}
XmlWriterUtil.writeLineBreak(writer);
XmlWriterUtil.writeCommentText(writer, comment, 2);
}
/**
* @param writer not null
* @param xmlnsPrefix could be null
* @param name not null
* @param value could be null. In this case, the element is not written.
* @throws IllegalArgumentException if name is null or empty
*/
public static void writeElement(XMLWriter writer, String xmlnsPrefix, String name, String value)
throws IllegalArgumentException {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name should be defined");
}
if (value != null) {
writeStartElement(writer, xmlnsPrefix, name);
writer.writeText(value);
writer.endElement();
}
}
/**
* @param writer not null
* @param xmlnsPrefix could be null
* @param name not null
* @param lang not null
* @param value could be null. In this case, the element is not written.
* @throws IllegalArgumentException if name is null or empty
*/
public static void writeElement(XMLWriter writer, String xmlnsPrefix, String name, String value, String lang)
throws IllegalArgumentException {
if (StringUtils.isEmpty(lang)) {
writeElement(writer, xmlnsPrefix, name, value);
return;
}
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name should be defined");
}
if (value != null) {
writeStartElement(writer, xmlnsPrefix, name);
writer.addAttribute("xml:lang", lang);
writer.writeText(value);
writer.endElement();
}
}
/**
* @param writer not null
* @param xmlnsPrefix could be null
* @param name not null
* @throws IllegalArgumentException if name is null or empty
* @since 1.1
*/
public static void writeStartElement(XMLWriter writer, String xmlnsPrefix, String name)
throws IllegalArgumentException {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name should be defined");
}
if (StringUtils.isNotEmpty(xmlnsPrefix)) {
writer.startElement(xmlnsPrefix + ":" + name);
} else {
writer.startElement(name);
}
}
/**
* @param writer not null
* @param xmlnsPrefix could be null
* @param name not null
* @param value could be null. In this case, the element is not written.
* @throws IllegalArgumentException if name is null or empty
*/
public static void writeRdfResourceElement(XMLWriter writer, String xmlnsPrefix, String name, String value)
throws IllegalArgumentException {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name should be defined");
}
if (value != null) {
writeStartElement(writer, xmlnsPrefix, name);
writer.addAttribute(RDF_RESOURCE, value);
writer.endElement();
}
}
/**
* @param writer not null
* @param name not null
* @param value could be null. In this case, the element is not written.
* @throws IllegalArgumentException if name is null or empty
*/
public static void writeRdfNodeIdElement(XMLWriter writer, String xmlnsPrefix, String name, String value)
throws IllegalArgumentException {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name should be defined");
}
if (value != null) {
writeStartElement(writer, xmlnsPrefix, name);
writer.addAttribute(RDF_NODE_ID, value);
writer.endElement();
}
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have a <code>developer</code> DOAP role.
*/
public static List<Contributor> getContributorsWithDeveloperRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("developers");
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have a <code>documenter</code> DOAP role.
*/
public static List<Contributor> getContributorsWithDocumenterRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("documenters");
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have an <code>helper</code> DOAP role.
*/
public static List<Contributor> getContributorsWithHelperRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("helpers");
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have a <code>maintainer</code> DOAP role.
*/
public static List<Contributor> getContributorsWithMaintainerRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("maintainers");
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have a <code>tester</code> DOAP role.
*/
public static List<Contributor> getContributorsWithTesterRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("testers");
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have a <code>translator</code> DOAP role.
*/
public static List<Contributor> getContributorsWithTranslatorRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("translators");
}
/**
* @param i18n the internationalization component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null list of <code>{@link Contributor}</code> which have an <code>unknown</code> DOAP role.
*/
public static List<Contributor> getContributorsWithUnknownRole(
I18N i18n, List<Contributor> developersOrContributors) {
return filterContributorsByDoapRoles(i18n, developersOrContributors).get("unknowns");
}
/**
* Utility class for keeping track of DOAP organizations in the DoaP mojo.
*
* @author <a href="mailto:t.fliss@gmail.com">Tim Fliss</a>
* @since 1.1
*/
public static class Organization {
private String name;
private String url;
private List<String> members = new LinkedList<>();
public Organization(String name, String url) {
this.name = name;
this.url = url;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public void addMember(String nodeId) {
members.add(nodeId);
}
public List<String> getMembers() {
return members;
}
}
/**
* put an organization from the pom file in the organization list.
*
* @param name from the pom file (e.g. Yoyodyne)
* @param url from the pom file (e.g. http://yoyodyne.example.org/about)
* @return the existing organization if a duplicate, or a new one.
*/
public static DoapUtil.Organization addOrganization(String name, String url) {
Organization organization = organizations.get(name);
if (organization == null) {
organization = new DoapUtil.Organization(name, url);
}
organizations.put(name, organization);
return organization;
}
// unique RDF blank node index scoped internal to the DOAP file
private static int nodeNumber = 1;
/**
* get a unique (within the DoaP file) RDF blank node ID
*
* @return the nodeID
* @see <a href="http://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-blank-nodes">
* http://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-blank-nodes</a>
*/
public static String getNodeId() {
return "b" + nodeNumber++;
}
/**
* get the set of Organizations that people are members of
*
* @return Map.EntrySet of DoapUtil.Organization
*/
public static Set<Entry<String, DoapUtil.Organization>> getOrganizations() {
return organizations.entrySet();
}
/**
* Validate the given DOAP file.
*
* @param doapFile not null and should exists.
* @return an empty list if the DOAP file is valid, otherwise a list of errors.
* @since 1.1
*/
public static List<String> validate(File doapFile) {
if (doapFile == null || !doapFile.isFile()) {
throw new IllegalArgumentException("The DOAP file should exist");
}
Model model = ModelFactory.createDefaultModel();
RDFReader r = model.getReader("RDF/XML");
r.setProperty("error-mode", "strict-error");
final List<String> errors = new ArrayList<>();
r.setErrorHandler(new RDFDefaultErrorHandler() {
@Override
public void error(Exception e) {
errors.add(e.getMessage());
}
});
try {
r.read(model, doapFile.toURI().toURL().toString());
} catch (MalformedURLException e) {
// ignored
}
return errors;
}
/**
* @param str not null
* @return <code>true</code> if the str parameter is a valid email, <code>false</code> otherwise.
* @since 1.1
*/
public static boolean isValidEmail(String str) {
if (StringUtils.isEmpty(str)) {
return false;
}
Matcher matcher = EMAIL_PATTERN.matcher(str);
return matcher.matches();
}
/**
* Fetch an URL
*
* @param settings the user settings used to fetch the url with an active proxy, if defined.
* @param url the url to fetch
* @throws IOException if any
* @see #DEFAULT_TIMEOUT
* @since 1.1
*/
public static void fetchURL(Settings settings, URL url) throws IOException {
if (url == null) {
throw new IllegalArgumentException("The url is null");
}
if ("file".equals(url.getProtocol())) {
InputStream in = null;
try {
in = url.openStream();
in.close();
in = null;
} finally {
IOUtil.close(in);
}
return;
}
// http, https...
HttpClient httpClient = new HttpClient(new MultiThreadedHttpConnectionManager());
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(DEFAULT_TIMEOUT);
httpClient.getHttpConnectionManager().getParams().setSoTimeout(DEFAULT_TIMEOUT);
httpClient.getParams().setBooleanParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, true);
// Some web servers don't allow the default user-agent sent by httpClient
httpClient
.getParams()
.setParameter(HttpMethodParams.USER_AGENT, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)");
if (settings != null && settings.getActiveProxy() != null) {
Proxy activeProxy = settings.getActiveProxy();
ProxyInfo proxyInfo = new ProxyInfo();
proxyInfo.setNonProxyHosts(activeProxy.getNonProxyHosts());
if (StringUtils.isNotEmpty(activeProxy.getHost())
&& !ProxyUtils.validateNonProxyHosts(proxyInfo, url.getHost())) {
httpClient.getHostConfiguration().setProxy(activeProxy.getHost(), activeProxy.getPort());
if (StringUtils.isNotEmpty(activeProxy.getUsername()) && activeProxy.getPassword() != null) {
Credentials credentials =
new UsernamePasswordCredentials(activeProxy.getUsername(), activeProxy.getPassword());
httpClient.getState().setProxyCredentials(AuthScope.ANY, credentials);
}
}
}
GetMethod getMethod = new GetMethod(url.toString());
try {
int status;
try {
status = httpClient.executeMethod(getMethod);
} catch (SocketTimeoutException e) {
// could be a sporadic failure, one more retry before we give up
status = httpClient.executeMethod(getMethod);
}
if (status != HttpStatus.SC_OK) {
throw new FileNotFoundException(url.toString());
}
} finally {
getMethod.releaseConnection();
}
}
/**
* Interpolate a string with project and settings.
*
* @param value could be null
* @param project not null
* @param settings could be null
* @return the value trimmed and interpolated or null if the interpolation doesn't work.
* @since 1.1
*/
public static String interpolate(String value, final MavenProject project, Settings settings) {
if (project == null) {
throw new IllegalArgumentException("project is required");
}
if (value == null) {
return value;
}
if (!value.contains("${")) {
return value.trim();
}
RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
try {
interpolator.addValueSource(new EnvarBasedValueSource());
} catch (IOException e) {
// ignore
}
interpolator.addValueSource(new PropertiesBasedValueSource(System.getProperties()));
interpolator.addValueSource(new PropertiesBasedValueSource(project.getProperties()));
interpolator.addValueSource(new PrefixedObjectValueSource("project", project));
interpolator.addValueSource(new PrefixedObjectValueSource("pom", project));
interpolator.addValueSource(new ObjectBasedValueSource(project) {
@Override
public Object getValue(String expression) {
try {
return ReflectionValueExtractor.evaluate(expression, project, true);
} catch (Exception e) {
addFeedback("Failed to extract \'" + expression + "\' from: " + project, e);
}
return null;
}
});
if (settings != null) {
interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));
}
String interpolatedValue = value;
try {
interpolatedValue = interpolator.interpolate(value).trim();
} catch (InterpolationException e) {
// ignore
}
if (interpolatedValue.startsWith("${")) {
return null;
}
return interpolatedValue;
}
// ----------------------------------------------------------------------
// Private methods
// ----------------------------------------------------------------------
/**
* Filter the developers/contributors roles by the keys from {@link I18N#getBundle()}. <br/>
* I18N roles supported in DOAP, i.e. <code>maintainer</code>, <code>developer</code>, <code>documenter</code>,
* <code>translator</code>, <code>tester</code>, <code>helper</code>. <br/>
* <b>Note:</b> Actually, only English keys are used.
*
* @param i18n i18n component
* @param developersOrContributors list of <code>{@link Contributor}</code>
* @return a none null map with <code>maintainers</code>, <code>developers</code>, <code>documenters</code>,
* <code>translators</code>, <code>testers</code>, <code>helpers</code>, <code>unknowns</code> as keys and
* list of <code>{@link Contributor}</code> as value.
*/
private static Map<String, List<Contributor>> filterContributorsByDoapRoles(
I18N i18n, List<Contributor> developersOrContributors) {
Map<String, List<Contributor>> returnMap = new HashMap<>(7);
returnMap.put("maintainers", new ArrayList<>());
returnMap.put("developers", new ArrayList<>());
returnMap.put("documenters", new ArrayList<>());
returnMap.put("translators", new ArrayList<>());
returnMap.put("testers", new ArrayList<>());
returnMap.put("helpers", new ArrayList<>());
returnMap.put("unknowns", new ArrayList<>());
if (developersOrContributors == null || developersOrContributors.isEmpty()) {
return returnMap;
}
for (Contributor contributor : developersOrContributors) {
List<String> roles = contributor.getRoles();
if (roles != null && roles.size() != 0) {
for (String role : roles) {
role = role.toLowerCase(Locale.ENGLISH);
if (role.contains(getLowerCaseString(i18n, "doap.maintainer"))) {
if (!returnMap.get("maintainers").contains(contributor)) {
returnMap.get("maintainers").add(contributor);
}
} else if (role.contains(getLowerCaseString(i18n, "doap.developer"))) {
if (!returnMap.get("developers").contains(contributor)) {
returnMap.get("developers").add(contributor);
}
} else if (role.contains(getLowerCaseString(i18n, "doap.documenter"))) {
if (!returnMap.get("documenters").contains(contributor)) {
returnMap.get("documenters").add(contributor);
}
} else if (role.contains(getLowerCaseString(i18n, "doap.translator"))) {
if (!returnMap.get("translators").contains(contributor)) {
returnMap.get("translators").add(contributor);
}
} else if (role.contains(getLowerCaseString(i18n, "doap.tester"))) {
if (!returnMap.get("testers").contains(contributor)) {
returnMap.get("testers").add(contributor);
}
} else if (role.contains(getLowerCaseString(i18n, "doap.helper"))) {
if (!returnMap.get("helpers").contains(contributor)) {
returnMap.get("helpers").add(contributor);
}
} else if (role.contains(getLowerCaseString(i18n, "doap.emeritus"))) {
// Don't add as developer nor as contributor as the person is no longer involved
} else {
if (!returnMap.get("unknowns").contains(contributor)) {
returnMap.get("unknowns").add(contributor);
}
}
}
} else {
if (!returnMap.get("unknowns").contains(contributor)) {
returnMap.get("unknowns").add(contributor);
}
}
}
return returnMap;
}
/**
* @param i18n not null
* @param key not null
* @return lower case value for the key in the i18n bundle.
*/
private static String getLowerCaseString(I18N i18n, String key) {
return i18n.getString("doap-person", Locale.ENGLISH, key).toLowerCase(Locale.ENGLISH);
}
/**
* @return the Maven artefact version.
*/
private static String getPluginVersion() {
Properties pomProperties = new Properties();
InputStream is = null;
try {
is = DoapUtil.class.getResourceAsStream(
"/META-INF/maven/org.apache.maven.plugins/" + "maven-doap-plugin/pom.properties");
if (is == null) {
return "<unknown>";
}
pomProperties.load(is);
is.close();
is = null;
return pomProperties.getProperty("version", "<unknown>");
} catch (IOException e) {
return "<unknown>";
} finally {
IOUtil.close(is);
}
}
/**
* Fork of {@link org.codehaus.plexus.interpolation.reflection.ReflectionValueExtractor} to care of list or arrays.
*/
static class ReflectionValueExtractor {
@SuppressWarnings("rawtypes")
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.
*/
@SuppressWarnings("rawtypes")
private static final Map<Class, ClassMap> CLASS_MAPS = new WeakHashMap<>();
private ReflectionValueExtractor() {}
public static Object evaluate(String expression, Object root) throws Exception {
return evaluate(expression, root, true);
}
// TODO: don't throw Exception
public static Object evaluate(String expression, Object root, boolean trimRootToken) throws Exception {
// if the root token refers to the supplied root object parameter, remove it.
if (trimRootToken) {
expression = expression.substring(expression.indexOf('.') + 1);
}
Object value = root;
// ----------------------------------------------------------------------
// Walk the dots and retrieve the ultimate value desired from the
// MavenProject instance.
// ----------------------------------------------------------------------
StringTokenizer parser = new StringTokenizer(expression, ".");
while (parser.hasMoreTokens()) {
String token = parser.nextToken();
if (value == null) {
return null;
}
StringTokenizer parser2 = new StringTokenizer(token, "[]");
int index = -1;
if (parser2.countTokens() > 1) {
token = parser2.nextToken();
try {
index = Integer.parseInt(parser2.nextToken());
} catch (NumberFormatException e) {
// ignore
}
}
final ClassMap classMap = getClassMap(value.getClass());
final String methodBase = StringUtils.capitalizeFirstLetter(token);
String methodName = "get" + methodBase;
Method method = classMap.findMethod(methodName, CLASS_ARGS);
if (method == null) {
// perhaps this is a boolean property??
methodName = "is" + methodBase;
method = classMap.findMethod(methodName, CLASS_ARGS);
}
if (method == null) {
return null;
}
value = method.invoke(value, OBJECT_ARGS);
if (value == null) {
return null;
}
if (Collection.class.isAssignableFrom(value.getClass())) {
ClassMap classMap2 = getClassMap(value.getClass());
Method method2 = classMap2.findMethod("toArray", CLASS_ARGS);
value = method2.invoke(value, OBJECT_ARGS);
}
if (value.getClass().isArray()) {
value = ((Object[]) value)[index];
}
}
return value;
}
private static ClassMap getClassMap(Class<? extends Object> clazz) {
ClassMap classMap = CLASS_MAPS.get(clazz);
if (classMap == null) {
classMap = new ClassMap(clazz);
CLASS_MAPS.put(clazz, classMap);
}
return classMap;
}
}
}