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