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;
014
015import java.lang.reflect.*;
016import java.util.*;
017
018import org.apache.juneau.json.*;
019
020/**
021 * Provides an {@link InvocationHandler} for creating beans from bean interfaces.
022 * 
023 * <p>
024 * If the {@code useInterfaceProxies} setting is enabled in {@link BeanContext}, this is the class that creates
025 * instances of beans from interfaces.
026 * 
027 * @param <T> The interface class
028 */
029public class BeanProxyInvocationHandler<T> implements InvocationHandler {
030
031   private final BeanMeta<T> meta;                 // The BeanMeta for this instance
032   private Map<String, Object> beanProps;    // The map of property names to bean property values.
033
034   /**
035    * Constructs with the specified {@link BeanMeta}.
036    * 
037    * @param meta The bean meta data.
038    */
039   public BeanProxyInvocationHandler(BeanMeta<T> meta) {
040      this.meta = meta;
041      this.beanProps = new HashMap<>();
042   }
043
044   /**
045    * Implemented to handle the method called.
046    */
047   @Override /* InvocationHandler */
048   public Object invoke(Object proxy, Method method, Object[] args) {
049      Class<?>[] paramTypes = method.getParameterTypes();
050      if (method.getName().equals("equals") && (paramTypes.length == 1) && (paramTypes[0] == java.lang.Object.class)) {
051         Object arg = args[0];
052         if (arg == null)
053            return false;
054         if (proxy == arg)
055            return true;
056         if (proxy.getClass() == arg.getClass()) {
057            InvocationHandler ih = Proxy.getInvocationHandler(arg);
058            if (ih instanceof BeanProxyInvocationHandler) {
059               return this.beanProps.equals(((BeanProxyInvocationHandler<?>)ih).beanProps);
060            }
061         }
062         BeanMap<Object> bean = this.meta.ctx.createSession().toBeanMap(arg);
063         return this.beanProps.equals(bean);
064      }
065
066      if (method.getName().equals("hashCode") && (paramTypes.length == 0))
067         return Integer.valueOf(this.beanProps.hashCode());
068
069      if (method.getName().equals("toString") && (paramTypes.length == 0))
070         return JsonSerializer.DEFAULT_LAX.toString(this.beanProps);
071
072      String prop = this.meta.getterProps.get(method);
073      if (prop != null)
074         return this.beanProps.get(prop);
075
076      prop = this.meta.setterProps.get(method);
077      if (prop != null) {
078         this.beanProps.put(prop, args[0]);
079         return null;
080      }
081
082      throw new UnsupportedOperationException("Unsupported bean method.  method=[ " + method + " ]");
083   }
084}