001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util.component; 018 019import java.util.ArrayList; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026/** 027 * Adds support for parameter name substitutions. 028 */ 029public class ArgumentSubstitutionParser<T> extends ApiMethodParser<T> { 030 031 private final Map<Pattern, Map<Pattern, List<NameReplacement>>> methodMap; 032 033 /** 034 * Create a parser using regular expressions to adapt parameter names. 035 * @param proxyType Proxy class. 036 * @param substitutions an array of <b>ordered</b> Argument adapters. 037 */ 038 public ArgumentSubstitutionParser(Class<T> proxyType, Substitution[] substitutions) { 039 super(proxyType); 040 Map<String, Map<String, List<NameReplacement>>> regexMap = new LinkedHashMap<>(); 041 042 for (Substitution substitution : substitutions) { 043 substitution.validate(); 044 045 final NameReplacement nameReplacement = new NameReplacement(); 046 nameReplacement.replacement = substitution.replacement; 047 if (substitution.argType != null) { 048 nameReplacement.typePattern = Pattern.compile(substitution.argType); 049 } 050 nameReplacement.replaceWithType = substitution.replaceWithType; 051 052 Map<String, List<NameReplacement>> replacementMap = regexMap.get(substitution.method); 053 if (replacementMap == null) { 054 replacementMap = new LinkedHashMap<>(); 055 regexMap.put(substitution.method, replacementMap); 056 } 057 List<NameReplacement> replacements = replacementMap.get(substitution.argName); 058 if (replacements == null) { 059 replacements = new ArrayList<>(); 060 replacementMap.put(substitution.argName, replacements); 061 } 062 replacements.add(nameReplacement); 063 } 064 065 // now compile the patterns, all this because Pattern doesn't override equals()!!! 066 this.methodMap = new LinkedHashMap<>(); 067 for (Map.Entry<String, Map<String, List<NameReplacement>>> method : regexMap.entrySet()) { 068 Map<Pattern, List<NameReplacement>> argMap = new LinkedHashMap<>(); 069 for (Map.Entry<String, List<NameReplacement>> arg : method.getValue().entrySet()) { 070 argMap.put(Pattern.compile(arg.getKey()), arg.getValue()); 071 } 072 methodMap.put(Pattern.compile(method.getKey()), argMap); 073 } 074 } 075 076 @Override 077 public List<ApiMethodModel> processResults(List<ApiMethodModel> parseResult) { 078 final List<ApiMethodModel> result = new ArrayList<>(); 079 080 for (ApiMethodModel model : parseResult) { 081 // look for method name matches 082 for (Map.Entry<Pattern, Map<Pattern, List<NameReplacement>>> methodEntry : methodMap.entrySet()) { 083 // match the whole method name 084 if (methodEntry.getKey().matcher(model.getName()).matches()) { 085 086 // look for arg name matches 087 final List<ApiMethodArg> updatedArguments = new ArrayList<>(); 088 final Map<Pattern, List<NameReplacement>> argMap = methodEntry.getValue(); 089 for (ApiMethodArg argument : model.getArguments()) { 090 091 final Class<?> argType = argument.getType(); 092 final String typeArgs = argument.getTypeArgs(); 093 final String argTypeName = argType.getCanonicalName(); 094 095 for (Map.Entry<Pattern, List<NameReplacement>> argEntry : argMap.entrySet()) { 096 final Matcher matcher = argEntry.getKey().matcher(argument.getName()); 097 098 // match argument name substring 099 if (matcher.find()) { 100 final List<NameReplacement> adapters = argEntry.getValue(); 101 for (NameReplacement adapter : adapters) { 102 if (adapter.typePattern == null) { 103 104 // no type pattern 105 final String newName = getJavaArgName(matcher.replaceAll(adapter.replacement)); 106 argument = new ApiMethodArg(newName, argType, typeArgs); 107 108 } else { 109 110 final Matcher typeMatcher = adapter.typePattern.matcher(argTypeName); 111 if (typeMatcher.find()) { 112 if (!adapter.replaceWithType) { 113 // replace argument name 114 final String newName = getJavaArgName(matcher.replaceAll(adapter.replacement)); 115 argument = new ApiMethodArg(newName, argType, typeArgs); 116 } else { 117 // replace name with argument type name 118 final String newName = getJavaArgName(typeMatcher.replaceAll(adapter.replacement)); 119 argument = new ApiMethodArg(newName, argType, typeArgs); 120 } 121 } 122 } 123 } 124 } 125 } 126 127 updatedArguments.add(argument); 128 } 129 130 model = new ApiMethodModel(model.getUniqueName(), model.getName(), model.getResultType(), 131 updatedArguments, model.getMethod()); 132 } 133 } 134 135 result.add(model); 136 } 137 138 return result; 139 } 140 141 private String getJavaArgName(String name) { 142 // make sure the first character is lowercase 143 // useful for replacement using type names 144 char firstChar = name.charAt(0); 145 if (Character.isLowerCase(firstChar)) { 146 return name; 147 } else { 148 return Character.toLowerCase(firstChar) + name.substring(1); 149 } 150 } 151 152 public static class Substitution { 153 154 private String method; 155 private String argName; 156 private String argType; 157 private String replacement; 158 private boolean replaceWithType; 159 160 /** 161 * Creates a substitution for all argument types. 162 * @param method regex to match method name 163 * @param argName regex to match argument name 164 * @param replacement replacement text for argument name 165 */ 166 public Substitution(String method, String argName, String replacement) { 167 this.method = method; 168 this.argName = argName; 169 this.replacement = replacement; 170 } 171 172 /** 173 * Creates a substitution for a specific argument type. 174 * @param method regex to match method name 175 * @param argName regex to match argument name 176 * @param argType argument type as String 177 * @param replacement replacement text for argument name 178 */ 179 public Substitution(String method, String argName, String argType, String replacement) { 180 this(method, argName, replacement); 181 this.argType = argType; 182 } 183 184 /** 185 * Create a substitution for a specific argument type and flag to indicate whether the replacement uses 186 * @param method 187 * @param argName 188 * @param argType 189 * @param replacement 190 * @param replaceWithType 191 */ 192 public Substitution(String method, String argName, String argType, String replacement, boolean replaceWithType) { 193 this(method, argName, argType, replacement); 194 this.replaceWithType = replaceWithType; 195 } 196 197 public void validate() { 198 if (method == null || argName == null || replacement == null) { 199 throw new IllegalArgumentException("Properties method, argName and replacement MUST be provided"); 200 } 201 } 202 } 203 204 private static class NameReplacement { 205 private String replacement; 206 private Pattern typePattern; 207 private boolean replaceWithType; 208 } 209}