blob: c0d0b0ed12ad213dfa2dfe5e60d62b1164dc5da0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.extensions.validator.core.el;
import org.apache.myfaces.extensions.validator.internal.ToDo;
import org.apache.myfaces.extensions.validator.internal.Priority;
import org.apache.myfaces.extensions.validator.internal.UsageInformation;
import org.apache.myfaces.extensions.validator.internal.UsageCategory;
import org.apache.myfaces.extensions.validator.util.ExtValUtils;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Iterator;
/**
* Helper class to get the real/full value binding - tested with facelets 1.1.14
* The target is to get rid of this impl. - currently it's a workaround to support custom facelets components.
* An alternative would be an EL-Resolver - there are still some open issues with such an approach
* + It isn't available with JSF 1.1.x
*
* @since 1.x.1
*/
@UsageInformation(UsageCategory.INTERNAL)
class FaceletsTaglibExpressionHelper
{
public static ValueBindingExpression tryToCreateValueBindingForFaceletsBinding(UIComponent uiComponent)
{
String faceletsValueBindingExpression = DefaultELHelper.getOriginalValueBindingExpression(uiComponent);
try
{
List<String> foundBindings = extractELTerms(
ExtValUtils.getELHelper().getBindingOfComponent(uiComponent, "value"));
Map<String, String> mappedFaceletsVars = new HashMap<String, String>();
ValueBindingExpression vbe= new ValueBindingExpression(faceletsValueBindingExpression
.substring(0, 1) + "{" + createBinding(foundBindings, mappedFaceletsVars) + "}");
Class entityClass = ExtValUtils.getELHelper()
.getTypeOfExpression(FacesContext.getCurrentInstance(), vbe.getBaseExpression());
if(entityClass == null)
{
return tryToReplaceVars(vbe, mappedFaceletsVars);
}
return vbe;
}
catch (Exception e)
{
return new ValueBindingExpression(faceletsValueBindingExpression);
}
}
@ToDo(value = Priority.MEDIUM, description = "logging")
private static String createBinding(List<String> expressions, Map<String, String> virtualVars)
{
String currentBinding;
int indexOfBindingDetails;
String[] foundBindingDetails;
String[] bindingDetails;
Map<String, String> addedVirtualNames = new HashMap<String, String>();
for (String entry : expressions)
{
if (entry.startsWith("ValueExpression["))
{
continue;
}
foundBindingDetails = entry.split(" ");
indexOfBindingDetails = findIndexOfBindingDetails(foundBindingDetails);
if (indexOfBindingDetails == -1)
{
return null;
}
bindingDetails = foundBindingDetails[indexOfBindingDetails].split("=");
if (bindingDetails.length < 2)
{
return null;
}
currentBinding = bindingDetails[1];
//to support blanks within a binding with map syntax
if(currentBinding.contains("{") && !currentBinding.contains("}"))
{
currentBinding = addFurtherBindingParts(currentBinding, foundBindingDetails, indexOfBindingDetails);
}
if (currentBinding.contains("}"))
{
//entry for "virtual" facelets beans
if(!addedVirtualNames.containsKey(bindingDetails[0]))
{
addedVirtualNames.put(bindingDetails[0], currentBinding);
}
}
//entry for "virtual" facelets var
if(!(currentBinding.contains("{") || currentBinding.contains("}")))
{
virtualVars.put(bindingDetails[0], bindingDetails[1].substring(1, bindingDetails[1].length()-2));
}
}
String originalBinding = addedVirtualNames.get("value");
originalBinding = originalBinding.substring(originalBinding.indexOf("{") + 1, originalBinding.indexOf("}"));
addedVirtualNames.remove("value");
return tryToTransformToRealBinding(originalBinding, addedVirtualNames, virtualVars);
}
private static String tryToTransformToRealBinding(
String originalBinding, Map<String, String> addedVirtualNames, Map<String, String> virtualVars)
{
originalBinding = "#{" + originalBinding + "}";
Iterator nameIterator = addedVirtualNames.keySet().iterator();
String currentKey;
String currentValue;
while(nameIterator.hasNext())
{
currentKey = (String) nameIterator.next();
currentValue = addedVirtualNames.get(currentKey);
currentValue = currentValue.substring(currentValue.indexOf("{") + 1, currentValue.indexOf("}"));
originalBinding = originalBinding.replace("{" + currentKey + ".", "{" + currentValue + ".");
//dynamic base and property
originalBinding = originalBinding.replace("{" + currentKey + "[", "{" + currentValue + "[");
originalBinding = originalBinding.replace("." + currentKey + ".", "." + currentValue + ".");
//dynamic base and property
originalBinding = originalBinding.replace("." + currentKey + "[", "." + currentValue + "[");
originalBinding = originalBinding.replace("[" + currentKey + "]", "['" + currentValue + "']");
//dynamic base and property
originalBinding = originalBinding.replace("[" + currentKey + "[", "[" + currentValue + "[");
originalBinding = originalBinding.replace("[" + currentKey + ".", "[" + currentValue + ".");
}
nameIterator = virtualVars.keySet().iterator();
while(nameIterator.hasNext())
{
currentKey = (String) nameIterator.next();
currentValue = virtualVars.get(currentKey);
originalBinding = originalBinding.replace("{" + currentKey + ".", "{" + currentValue + ".");
//dynamic base and property
originalBinding = originalBinding.replace("{" + currentKey + "[", "{" + currentValue + "[");
originalBinding = originalBinding.replace("." + currentKey + ".", "." + currentValue + ".");
//dynamic base and property
originalBinding = originalBinding.replace("." + currentKey + "[", "." + currentValue + "[");
originalBinding = originalBinding.replace("[" + currentKey + "]", "['" + currentValue + "']");
//dynamic base and property
originalBinding = originalBinding.replace("[" + currentKey + "[", "[" + currentValue + "[");
originalBinding = originalBinding.replace("[" + currentKey + ".", "[" + currentValue + ".");
}
return originalBinding.substring(2, originalBinding.length() - 1);
}
//to support blanks - e.g. with map syntax
private static String addFurtherBindingParts(String currentBinding, String[] foundBindingDetails,
int indexOfBindingDetails)
{
for(int i = indexOfBindingDetails + 1; i < foundBindingDetails.length; i++)
{
currentBinding += foundBindingDetails[i];
if(foundBindingDetails[i].contains("}"))
{
return currentBinding;
}
}
return currentBinding;
}
private static int findIndexOfBindingDetails(String[] bindingDetails)
{
int count = 0;
for (String entry : bindingDetails)
{
if (entry.contains("="))
{
return count;
}
count++;
}
return -1;
}
private static List<String> extractELTerms(Object o)
{
List<String> foundELTerms = new ArrayList<String>();
try
{
if (resolveELTerms(o, new HashMap<Object, Object>(), foundELTerms, 0) > 0)
{
return foundELTerms;
}
}
catch (Exception ex)
{
return null;
}
return null;
}
private static int resolveELTerms(Object o, Map<Object, Object> visited,
List<String> foundELTerms, int count) throws Exception
{
if (o == null || visited.containsKey(o) || count > 50)
{
return 0;
}
visited.put(o, null);
int elCount = 0;
Class c = o.getClass();
//inspect maps
if (o instanceof Map)
{
for (Object entry : ((Map) o).values())
{
//found entry for "virtual" facelets var
if(entry.toString().contains("ValueExpression["))
{
foundELTerms.add(entry.toString());
}
elCount += resolveELTerms(entry, visited, foundELTerms, count + 1);
}
return elCount;
}
if (ExtValUtils.getELHelper().isELTermWellFormed(o))
{
if (foundELTerms != null)
{
foundELTerms.add(o.toString());
}
return ++elCount;
}
//analyze arrays
if (c.isArray())
{
int length = Array.getLength(o);
//check array [L -> no array of primitive types
if (o.toString().startsWith("[L"))
{
for (int i = 0; i < length; i++)
{
if (o.toString().startsWith("[Ljava.lang.String"))
{
if (ExtValUtils.getELHelper().isELTermWellFormed(Array.get(o, i)))
{
if (foundELTerms != null)
{
foundELTerms.add(o.toString());
}
elCount++;
}
}
else
{
elCount += resolveELTerms(Array.get(o, i), visited, foundELTerms, count + 1);
}
}
}
return elCount;
}
List<Field> attributes = findAllAttributes(c, new ArrayList<Field>());
Field[] fields = attributes.toArray(new Field[attributes.size()]);
AccessibleObject.setAccessible(fields, true);
for (Field currentField : fields)
{
if (currentField.get(o) == null)
{
continue;
}
if (currentField.getType().equals(String.class))
{
if (currentField.get(o) != null && ExtValUtils.getELHelper().isELTermWellFormed(currentField.get(o)))
{
if (foundELTerms != null)
{
foundELTerms.add(o.toString());
}
elCount++;
}
}
else if (!currentField.getType().isPrimitive())
{
elCount += resolveELTerms(currentField.get(o), visited, foundELTerms, count + 1);
}
}
return elCount;
}
private static List<Field> findAllAttributes(Class c, List<Field> attributes)
{
if (c == null)
{
return attributes;
}
findAllAttributes(c.getSuperclass(), attributes);
Field[] fields = c.getDeclaredFields();
for (Field currentField : fields)
{
if (!Modifier.isStatic(currentField.getModifiers()))
{
attributes.add(currentField);
}
}
return attributes;
}
private static ValueBindingExpression tryToReplaceVars(ValueBindingExpression valueBindingExpression,
Map<String, String> mappedFaceletsVars)
{
String property;
String result = "";
boolean last = false;
while(true)
{
if(valueBindingExpression.getBaseExpression() == null)
{
last = true;
}
property = valueBindingExpression.getProperty();
valueBindingExpression = ValueBindingExpression
.replaceProperty(valueBindingExpression, getNewProperty(property, mappedFaceletsVars));
if(result.length() == 0)
{
result = valueBindingExpression.getProperty();
}
else
{
result = valueBindingExpression.getProperty() + "." + result;
}
valueBindingExpression = valueBindingExpression.getBaseExpression();
if(last)
{
break;
}
}
return new ValueBindingExpression(valueBindingExpression.getPrefix() + "{" + result + "}");
}
private static String getNewProperty(String oldProperty, Map<String, String> mappedFaceletsVars)
{
List<String> virtualVars = getPotentialVirtualVars(oldProperty);
for(String virtualVar : virtualVars)
{
if(mappedFaceletsVars.containsKey(virtualVar))
{
oldProperty = replacePropertyValue(oldProperty, virtualVar, mappedFaceletsVars.get(virtualVar));
}
}
return oldProperty;
}
private static List<String> getPotentialVirtualVars(String oldProperty)
{
int start = -1;
int end = -1;
List<String> virtualVarList = new ArrayList<String>();
for(int i = 0; i < oldProperty.length(); i++)
{
if(start == - 1 && oldProperty.charAt(i) == '[')
{
start = i + 1;
}
else if((start != - 1 && oldProperty.charAt(i) == '[') || oldProperty.charAt(i) == ']')
{
end = i;
}
if(start != -1 && end != -1)
{
virtualVarList.add(oldProperty.substring(start, end));
if(oldProperty.charAt(i) == '[')
{
start = i + 1;
}
else
{
start = -1;
}
end = -1;
}
}
return virtualVarList;
}
private static String replacePropertyValue(String oldProperty, String targetVar, String newValue)
{
int index = oldProperty.indexOf(targetVar);
if(index == -1)
{
return oldProperty;
}
String result = oldProperty.substring(0, index);
result += newValue;
return result + oldProperty.substring(index + targetVar.length(), oldProperty.length());
}
/*
* replace virtual facelets vars (map syntax)
* tested styles (simple and nested): test[ix[ix2[ix3]]]
*/
/*
private static String _createBinding(List<String> expressions, Map<String, String> virtualVars)
{
String result = "";
String prevFaceletsAttributeName = null;
String currentBinding;
String partOfBinding;
int indexOfBindingDetails;
String[] foundBindingDetails;
String[] bindingDetails;
List<String> addedVirtualNames = new ArrayList<String>();
for (String entry : expressions)
{
if (entry.startsWith("ValueExpression["))
{
continue;
}
foundBindingDetails = entry.split(" ");
indexOfBindingDetails = findIndexOfBindingDetails(foundBindingDetails);
if (indexOfBindingDetails == -1)
{
return null;
}
bindingDetails = foundBindingDetails[indexOfBindingDetails].split("=");
if (bindingDetails.length < 2)
{
return null;
}
currentBinding = bindingDetails[1];
//to support blanks within a binding with map syntax
if(currentBinding.contains("{") && !currentBinding.contains("}"))
{
currentBinding = addFurtherBindingParts(currentBinding, foundBindingDetails, indexOfBindingDetails);
}
if (prevFaceletsAttributeName != null && currentBinding.contains("}"))
{
//entry for "virtual" facelets beans
if(!addedVirtualNames.contains(bindingDetails[0]))
{
partOfBinding = currentBinding.substring(currentBinding.indexOf(prevFaceletsAttributeName)
+ prevFaceletsAttributeName.length(), currentBinding.indexOf("}"));
addedVirtualNames.add(bindingDetails[0]);
result = result + partOfBinding;
}
else
{
continue;
}
}
//entry for "virtual" facelets var
else if(!(currentBinding.contains("{") || currentBinding.contains("}")))
{
virtualVars.put(bindingDetails[0], bindingDetails[1].substring(1, bindingDetails[1].length()-2));
continue;
}
else
{
if(!addedVirtualNames.contains(bindingDetails[0]))
{
addedVirtualNames.add(bindingDetails[0]);
result = currentBinding.substring(currentBinding.indexOf("{") + 1, currentBinding.indexOf("}"));
}
}
prevFaceletsAttributeName = bindingDetails[0];
}
return result;
}
*/
}