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.utils;
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 * Used to invoke methods on {@code Objects} using arguments in serialized form.
024 *
025 * <h5 class='section'>Example:</h5>
026 * <p class='bcode w800'>
027 *    String s = <js>"foobar"</js>;
028 *    String s2 = (String)<jk>new</jk> PojoIntrospector(s)
029 *       .invoke(<js>"substring(int,int)"</js>, <js>"[3,6]"</js>);  <jc>// "bar"</jc>
030 * </p>
031 */
032public final class PojoIntrospector {
033
034   private final Object o;
035   private final ReaderParser p;
036
037   /**
038    * Constructor.
039    *
040    * @param o The object on which Java methods will be invoked.
041    * @param p The parser to use to parse the method arguments.
042    * If <jk>null</jk>, {@link JsonParser#DEFAULT} is used.
043    */
044   public PojoIntrospector(Object o, ReaderParser p) {
045      if (p == null)
046         p = JsonParser.DEFAULT;
047      this.o = o;
048      this.p = p;
049   }
050
051   /**
052    * Shortcut for calling <code><jk>new</jk> PojoIntrospector(o, <jk>null</jk>);</code>
053    *
054    * @param o The object on which Java methods will be invoked.
055    */
056   public PojoIntrospector(Object o) {
057      this(o, null);
058   }
059
060   /**
061    * Primary method.
062    *
063    * <p>
064    * Invokes the specified method on this bean.
065    *
066    * @param method The method being invoked.
067    * @param args
068    *    The arguments to pass as parameters to the method.
069    *    These will automatically be converted to the appropriate object type if possible.
070    *    Can be <jk>null</jk> if method has no arguments.
071    * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>.
072    * @throws IllegalAccessException
073    *    If the <c>Constructor</c> object enforces Java language access control and the underlying constructor is
074    *    inaccessible.
075    * @throws IllegalArgumentException
076    *    If one of the following occurs:
077    *    <ul class='spaced-list'>
078    *       <li>
079    *          The number of actual and formal parameters differ.
080    *       <li>
081    *          An unwrapping conversion for primitive arguments fails.
082    *       <li>
083    *          A parameter value cannot be converted to the corresponding formal parameter type by a method invocation
084    *          conversion.
085    *       <li>
086    *          The constructor pertains to an enum type.
087    *    </ul>
088    * @throws InvocationTargetException If the underlying constructor throws an exception.
089    * @throws ParseException Malformed input encountered.
090    * @throws IOException Thrown by underlying stream.
091    */
092   public Object invokeMethod(Method method, Reader args) throws InvocationTargetException, IllegalArgumentException,
093         IllegalAccessException, ParseException, IOException {
094      if (o == null)
095         return null;
096      Object[] params = args == null ? null : p.parseArgs(args, method.getGenericParameterTypes());
097      return method.invoke(o, params);
098   }
099
100   /**
101    * Convenience method for invoking argument from method signature (@see {@link MethodInfo#getSignature()}.
102    *
103    * @param method The method being invoked.
104    * @param args
105    *    The arguments to pass as parameters to the method.
106    *    These will automatically be converted to the appropriate object type if possible.
107    *    Can be <jk>null</jk> if method has no arguments.
108    * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>.
109    * @throws NoSuchMethodException If method does not exist.
110    * @throws IllegalAccessException
111    *    If the <c>Constructor</c> object enforces Java language access control and
112    *    the underlying constructor is inaccessible.
113    * @throws IllegalArgumentException
114    *    If one of the following occurs:
115    *    <ul class='spaced-list'>
116    *       <li>
117    *          The number of actual and formal parameters differ.
118    *       <li>
119    *          An unwrapping conversion for primitive arguments fails.
120    *       <li>
121    *          A parameter value cannot be converted to the corresponding formal parameter type by a method invocation
122    *          conversion.
123    *       <li>
124    *          The constructor pertains to an enum type.
125    *    </ul>
126    * @throws InvocationTargetException If the underlying constructor throws an exception.
127    * @throws ParseException Malformed input encountered.
128    * @throws IOException Thrown by underlying stream.
129    */
130   public Object invokeMethod(String method, String args) throws NoSuchMethodException, IllegalArgumentException,
131         InvocationTargetException, IllegalAccessException, ParseException, IOException {
132      if (o == null)
133         return null;
134      Method m = p.getClassMeta(o.getClass()).getPublicMethods().get(method);
135      if (m == null)
136         throw new NoSuchMethodException(method);
137      return invokeMethod(m, args == null ? null : new StringReader(args));
138   }
139}