001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.objecttools; 014 015import java.io.*; 016import java.lang.reflect.*; 017 018import org.apache.juneau.json.*; 019import org.apache.juneau.parser.*; 020import org.apache.juneau.reflect.*; 021 022/** 023 * POJO method introspector. 024 * 025 * <p> 026 * This class is used to invoke methods on {@code Objects} using arguments in serialized form. 027 * </p> 028 * 029 * <h5 class='section'>Example:</h5> 030 * <p class='bjava'> 031 * String <jv>string1</jv> = <js>"foobar"</js>; 032 * String <jv>string2</jv> = ObjectIntrospector 033 * .create(<jv>string</jv>) 034 * .invoke(String.<jk>class</jk>, <js>"substring(int,int)"</js>, <js>"[3,6]"</js>); <jc>// "bar"</jc> 035 * </p> 036 * <p> 037 * The arguments passed to the identified method are POJOs serialized in JSON format. Arbitrarily complex arguments can be passed 038 * in as arguments. 039 * </p> 040 * <ul> 041 * <li class='warn'>This is an extremely powerful but potentially dangerous tool. Use wisely. 042 * </ul> 043 * 044 * <h5 class='section'>See Also:</h5><ul> 045 046 * </ul> 047 */ 048public final class ObjectIntrospector { 049 050 //----------------------------------------------------------------------------------------------------------------- 051 // Static 052 //----------------------------------------------------------------------------------------------------------------- 053 054 /** 055 * Static creator. 056 * @param o The object on which Java methods will be invoked. 057 * @return A new {@link ObjectIntrospector} object. 058 */ 059 public static ObjectIntrospector create(Object o) { 060 return new ObjectIntrospector(o); 061 } 062 063 /** 064 * Static creator. 065 * @param o The object on which Java methods will be invoked. 066 * @param parser The parser to use to parse the method arguments. 067 * @return A new {@link ObjectIntrospector} object. 068 */ 069 public static ObjectIntrospector create(Object o, ReaderParser parser) { 070 return new ObjectIntrospector(o, parser); 071 } 072 073 //----------------------------------------------------------------------------------------------------------------- 074 // Instance 075 //----------------------------------------------------------------------------------------------------------------- 076 077 private final Object object; 078 private final ReaderParser parser; 079 080 /** 081 * Constructor. 082 * 083 * @param object The object on which Java methods will be invoked. 084 * @param parser The parser to use to parse the method arguments. 085 * If <jk>null</jk>, {@link JsonParser#DEFAULT} is used. 086 */ 087 public ObjectIntrospector(Object object, ReaderParser parser) { 088 if (parser == null) 089 parser = JsonParser.DEFAULT; 090 this.object = object; 091 this.parser = parser; 092 } 093 094 /** 095 * Shortcut for calling <code><jk>new</jk> ObjectIntrospector(o, <jk>null</jk>);</code> 096 * 097 * @param o The object on which Java methods will be invoked. 098 */ 099 public ObjectIntrospector(Object o) { 100 this(o, null); 101 } 102 103 /** 104 * Primary method. 105 * 106 * <p> 107 * Invokes the specified method on this bean. 108 * 109 * @param method The method being invoked. 110 * @param args 111 * The arguments to pass as parameters to the method. 112 * These will automatically be converted to the appropriate object type if possible. 113 * Can be <jk>null</jk> if method has no arguments. 114 * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>. 115 * @throws IllegalAccessException 116 * If the <c>Constructor</c> object enforces Java language access control and the underlying constructor is 117 * inaccessible. 118 * @throws IllegalArgumentException 119 * If one of the following occurs: 120 * <ul class='spaced-list'> 121 * <li> 122 * The number of actual and formal parameters differ. 123 * <li> 124 * An unwrapping conversion for primitive arguments fails. 125 * <li> 126 * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation 127 * conversion. 128 * <li> 129 * The constructor pertains to an enum type. 130 * </ul> 131 * @throws InvocationTargetException If the underlying constructor throws an exception. 132 * @throws ParseException Malformed input encountered. 133 * @throws IOException Thrown by underlying stream. 134 */ 135 public Object invokeMethod(Method method, Reader args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, ParseException, IOException { 136 if (object == null) 137 return null; 138 Object[] params = args == null ? null : parser.parseArgs(args, method.getGenericParameterTypes()); 139 return method.invoke(object, params); 140 } 141 142 /** 143 * Primary method. 144 * 145 * <p> 146 * Invokes the specified method on this bean. 147 * 148 * @param <T> The return type of the method call. 149 * @param returnType The return type of the method call. 150 * @param method The method being invoked. 151 * @param args 152 * The arguments to pass as parameters to the method. 153 * These will automatically be converted to the appropriate object type if possible. 154 * Can be <jk>null</jk> if method has no arguments. 155 * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>. 156 * @throws IllegalAccessException 157 * If the <c>Constructor</c> object enforces Java language access control and the underlying constructor is 158 * inaccessible. 159 * @throws IllegalArgumentException 160 * If one of the following occurs: 161 * <ul class='spaced-list'> 162 * <li> 163 * The number of actual and formal parameters differ. 164 * <li> 165 * An unwrapping conversion for primitive arguments fails. 166 * <li> 167 * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation 168 * conversion. 169 * <li> 170 * The constructor pertains to an enum type. 171 * </ul> 172 * @throws InvocationTargetException If the underlying constructor throws an exception. 173 * @throws ParseException Malformed input encountered. 174 * @throws IOException Thrown by underlying stream. 175 */ 176 public <T> T invokeMethod(Class<T> returnType, Method method, Reader args) throws InvocationTargetException, IllegalArgumentException, IllegalAccessException, ParseException, IOException { 177 return returnType.cast(invokeMethod(method, args)); 178 } 179 180 /** 181 * Convenience method for invoking argument from method signature (@see {@link MethodInfo#getSignature()}. 182 * 183 * @param method The method being invoked. 184 * @param args 185 * The arguments to pass as parameters to the method. 186 * These will automatically be converted to the appropriate object type if possible. 187 * Can be <jk>null</jk> if method has no arguments. 188 * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>. 189 * @throws NoSuchMethodException If method does not exist. 190 * @throws IllegalAccessException 191 * If the <c>Constructor</c> object enforces Java language access control and 192 * the underlying constructor is inaccessible. 193 * @throws IllegalArgumentException 194 * If one of the following occurs: 195 * <ul class='spaced-list'> 196 * <li> 197 * The number of actual and formal parameters differ. 198 * <li> 199 * An unwrapping conversion for primitive arguments fails. 200 * <li> 201 * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation 202 * conversion. 203 * <li> 204 * The constructor pertains to an enum type. 205 * </ul> 206 * @throws InvocationTargetException If the underlying constructor throws an exception. 207 * @throws ParseException Malformed input encountered. 208 * @throws IOException Thrown by underlying stream. 209 */ 210 public Object invokeMethod(String method, String args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException, IllegalAccessException, ParseException, IOException { 211 if (object == null) 212 return null; 213 Method m = parser.getBeanContext().getClassMeta(object.getClass()).getPublicMethods().get(method); 214 if (m == null) 215 throw new NoSuchMethodException(method); 216 return invokeMethod(m, args == null ? null : new StringReader(args)); 217 } 218 219 /** 220 * Convenience method for invoking argument from method signature (@see {@link MethodInfo#getSignature()}. 221 * 222 * @param <T> The return type of the method call. 223 * @param returnType The return type of the method call. 224 * @param method The method being invoked. 225 * @param args 226 * The arguments to pass as parameters to the method. 227 * These will automatically be converted to the appropriate object type if possible. 228 * Can be <jk>null</jk> if method has no arguments. 229 * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>. 230 * @throws NoSuchMethodException If method does not exist. 231 * @throws IllegalAccessException 232 * If the <c>Constructor</c> object enforces Java language access control and 233 * the underlying constructor is inaccessible. 234 * @throws IllegalArgumentException 235 * If one of the following occurs: 236 * <ul class='spaced-list'> 237 * <li> 238 * The number of actual and formal parameters differ. 239 * <li> 240 * An unwrapping conversion for primitive arguments fails. 241 * <li> 242 * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation 243 * conversion. 244 * <li> 245 * The constructor pertains to an enum type. 246 * </ul> 247 * @throws InvocationTargetException If the underlying constructor throws an exception. 248 * @throws ParseException Malformed input encountered. 249 * @throws IOException Thrown by underlying stream. 250 */ 251 public <T> T invokeMethod(Class<T> returnType, String method, String args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException, IllegalAccessException, ParseException, IOException { 252 return returnType.cast(invokeMethod(method, args)); 253 } 254}