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.internal.*;
019import org.apache.juneau.json.*;
020import org.apache.juneau.parser.*;
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'>
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.  Invokes the specified method on this bean.
062    * 
063    * @param method The method being invoked.
064    * @param args
065    *    The arguments to pass as parameters to the method.
066    *    These will automatically be converted to the appropriate object type if possible.
067    *    Can be <jk>null</jk> if method has no arguments.
068    * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>.
069    * @throws IllegalAccessException
070    *    If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is
071    *    inaccessible.
072    * @throws IllegalArgumentException
073    *    If one of the following occurs:
074    *    <ul class='spaced-list'>
075    *       <li>
076    *          The number of actual and formal parameters differ.
077    *       <li>
078    *          An unwrapping conversion for primitive arguments fails.
079    *       <li>
080    *          A parameter value cannot be converted to the corresponding formal parameter type by a method invocation
081    *          conversion.
082    *       <li>
083    *          The constructor pertains to an enum type.
084    *    </ul>
085    * @throws InvocationTargetException If the underlying constructor throws an exception.
086    * @throws ParseException If the input contains a syntax error or is malformed.
087    * @throws IOException
088    */
089   public Object invokeMethod(Method method, Reader args) throws InvocationTargetException, IllegalArgumentException,
090         IllegalAccessException, ParseException, IOException {
091      if (o == null)
092         return null;
093      Object[] params = args == null ? null : p.parseArgs(args, method.getGenericParameterTypes());
094      return method.invoke(o, params);
095   }
096
097   /**
098    * Convenience method for invoking argument from method signature (@see {@link ClassUtils#getMethodSignature(Method)}.
099    * 
100    * @param method The method being invoked.
101    * @param args
102    *    The arguments to pass as parameters to the method.
103    *    These will automatically be converted to the appropriate object type if possible.
104    *    Can be <jk>null</jk> if method has no arguments.
105    * @return The object returned by the call to the method, or <jk>null</jk> if target object is <jk>null</jk>.
106    * @throws NoSuchMethodException If method does not exist.
107    * @throws IllegalAccessException
108    *    If the <code>Constructor</code> object enforces Java language access control and
109    *    the underlying constructor is inaccessible.
110    * @throws IllegalArgumentException
111    *    If one of the following occurs:
112    *    <ul class='spaced-list'>
113    *       <li>
114    *          The number of actual and formal parameters differ.
115    *       <li>
116    *          An unwrapping conversion for primitive arguments fails.
117    *       <li>
118    *          A parameter value cannot be converted to the corresponding formal parameter type by a method invocation
119    *          conversion.
120    *       <li>
121    *          The constructor pertains to an enum type.
122    *    </ul>
123    * @throws InvocationTargetException If the underlying constructor throws an exception.
124    * @throws ParseException If the input contains a syntax error or is malformed.
125    * @throws IOException
126    */
127   public Object invokeMethod(String method, String args) throws NoSuchMethodException, IllegalArgumentException,
128         InvocationTargetException, IllegalAccessException, ParseException, IOException {
129      if (o == null)
130         return null;
131      Method m = p.getClassMeta(o.getClass()).getPublicMethods().get(method);
132      if (m == null)
133         throw new NoSuchMethodException(method);
134      return invokeMethod(m, args == null ? null : new StringReader(args));
135   }
136}