package org.jboss.seam.faces.component;

import java.beans.FeatureDescriptor;
import java.util.Iterator;
import java.util.Locale;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.FunctionMapper;
import javax.el.ValueExpression;
import javax.el.ValueReference;
import javax.el.VariableMapper;
import javax.faces.el.CompositeComponentExpressionHolder;

/**
 * Analyzes a {@link ValueExpression} and provides access to the base object and property
 * name to which the expression maps via the getReference() method.
 */
class ValueExpressionAnalyzer
{
   private ValueExpression expression;

   public ValueExpressionAnalyzer(ValueExpression expression)
   {
      this.expression = expression;
   }

   public ValueReference getValueReference(ELContext elContext)
   {
      InterceptingResolver resolver = new InterceptingResolver(elContext.getELResolver());
      try
      {
         expression.setValue(decorateELContext(elContext, resolver), null);
      }
      catch (ELException ele)
      {
         return null;
      }
      ValueReference reference = resolver.getValueReference();
      if (reference != null)
      {
         Object base = reference.getBase();
         if (base instanceof CompositeComponentExpressionHolder)
         {
            ValueExpression ve = ((CompositeComponentExpressionHolder) base).getExpression((String) reference.getProperty());
            if (ve != null)
            {
               this.expression = ve;
               reference = getValueReference(elContext);
            }
         }
      }
      return reference;
   }

   private ELContext decorateELContext(final ELContext context, final ELResolver resolver)
   {
      return new ELContext()
      {
         // punch in our new ELResolver
         @Override
         public ELResolver getELResolver()
         {
            return resolver;
         }

         // The rest of the methods simply delegate to the existing context
         @Override
         public Object getContext(Class key)
         {
            return context.getContext(key);
         }

         @Override
         public Locale getLocale()
         {
            return context.getLocale();
         }

         @Override
         public boolean isPropertyResolved()
         {
            return context.isPropertyResolved();
         }

         @Override
         public void putContext(Class key, Object contextObject)
         {
            context.putContext(key, contextObject);
         }

         @Override
         public void setLocale(Locale locale)
         {
            context.setLocale(locale);
         }

         @Override
         public void setPropertyResolved(boolean resolved)
         {
            context.setPropertyResolved(resolved);
         }

         @Override
         public FunctionMapper getFunctionMapper()
         {
            return context.getFunctionMapper();
         }

         @Override
         public VariableMapper getVariableMapper()
         {
            return context.getVariableMapper();
         }
      };
   }

   private static class InterceptingResolver extends ELResolver
   {
      private ELResolver delegate;
      private ValueReference valueReference;

      public InterceptingResolver(ELResolver delegate)
      {
         this.delegate = delegate;
      }

      public ValueReference getValueReference()
      {
         return valueReference;
      }

      // Capture the base and property rather than write the value
      @Override
      public void setValue(ELContext context, Object base, Object property, Object value)
      {
         if (base != null && property != null)
         {
            context.setPropertyResolved(true);
            valueReference = new ValueReference(base, property.toString());
         }
      }

      // The rest of the methods simply delegate to the existing context
      @Override
      public Object getValue(ELContext context, Object base, Object property)
      {
         return delegate.getValue(context, base, property);
      }

      @Override
      public Class<?> getType(ELContext context, Object base, Object property)
      {
         return delegate.getType(context, base, property);
      }

      @Override
      public boolean isReadOnly(ELContext context, Object base, Object property)
      {
         return delegate.isReadOnly(context, base, property);
      }

      @Override
      public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base)
      {
         return delegate.getFeatureDescriptors(context, base);
      }

      @Override
      public Class<?> getCommonPropertyType(ELContext context, Object base)
      {
         return delegate.getCommonPropertyType(context, base);
      }
   }
}

@@ -1,8 +1,1012 @@
+/*%0A * JBoss, Home of Professional Open Source%0A * Copyright 2010, Red Hat, Inc., and individual contributors%0A * by the @authors tag. See the copyright.txt in the distribution for a%0A * full listing of individual contributors.%0A *%0A * This is free software; you can redistribute it and/or modify it%0A * under the terms of the GNU Lesser General Public License as%0A * published by the Free Software Foundation; either version 2.1 of%0A * the License, or (at your option) any later version.%0A *%0A * This software is distributed in the hope that it will be useful,%0A * but WITHOUT ANY WARRANTY; without even the implied warranty of%0A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU%0A * Lesser General Public License for more details.%0A *%0A * You should have received a copy of the GNU Lesser General Public%0A * License along with this software; if not, write to the Free%0A * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA%0A * 02110-1301 USA, or see the FSF site: http://www.fsf.org.%0A */%0A
 package 
@@ -1406,16 +1406,19 @@
 %0A/**%0A * 
+%3Cp%3E
 Analyzes
@@ -1499,76 +1499,488 @@
 erty
-%0A * name to which the expression maps via the getReference() method.
+ name%0A * referenced by the expression.%3C/p%3E%0A * %0A * %3Cp%3EThe getValueReference(ELContext) method returns a %7B@link ValueReference%7D object, which%0A * encapsulates the base object and property name to which the expression maps. This process%0A * works by resolving the expression up until the last segment.%3C/p%3E%0A *%0A * %3Cp%3EAlthough access to the ValueReference was added in EL 2.2, the feature%0A * does not work correctly, which is why this custom class is required.%3C/p%3E%0A *%0A * @author Dan Allen
 %0A */

add license header, author and javadoc