/* | |
* 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.util; | |
import org.apache.commons.logging.Log; | |
import org.apache.commons.logging.LogFactory; | |
import org.apache.myfaces.extensions.validator.core.InformationProviderBean; | |
import org.apache.myfaces.extensions.validator.core.ProcessedInformationEntry; | |
import org.apache.myfaces.extensions.validator.core.WebXmlParameter; | |
import javax.faces.FactoryFinder; | |
import javax.faces.application.Application; | |
import javax.faces.component.EditableValueHolder; | |
import javax.faces.component.UIComponent; | |
import javax.faces.component.UIViewRoot; | |
import javax.faces.component.ValueHolder; | |
import javax.faces.context.FacesContext; | |
import javax.faces.convert.Converter; | |
import javax.faces.event.PhaseListener; | |
import javax.faces.lifecycle.Lifecycle; | |
import javax.faces.lifecycle.LifecycleFactory; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
/** | |
* @author Gerhard Petracek | |
*/ | |
public class ExtValUtils | |
{ | |
private static final Log LOGGER = LogFactory.getLog(ExtValUtils.class); | |
//TODO test | |
public static void createValueBindingConvertedValueMapping( | |
UIComponent uiComponent, Object convertedObject) | |
{ | |
if (!(uiComponent instanceof EditableValueHolder)) | |
{ | |
return; | |
} | |
//to support local cross-validation (within the same entity) | |
Map<String, ProcessedInformationEntry> valueBindingConvertedValueMapping = ExtValUtils | |
.getOrInitValueBindingConvertedValueMapping(); | |
String valueBindingExpression; | |
ProcessedInformationEntry entry; | |
valueBindingExpression = ELUtils | |
.getReliableValueBindingExpression(uiComponent); | |
if (valueBindingExpression == null) | |
{ | |
return; | |
} | |
entry = new ProcessedInformationEntry(); | |
entry.setBean(ELUtils.getBaseObject(valueBindingExpression, uiComponent)); | |
entry.setConvertedValue(convertedObject); | |
entry.setComponent(uiComponent); | |
//for local cross-validation | |
if (valueBindingConvertedValueMapping | |
.containsKey(valueBindingExpression) | |
&& !valueBindingConvertedValueMapping.get( | |
valueBindingExpression).getBean().equals( | |
entry.getBean())) | |
{ | |
//for the validation within a complex component e.g. a table | |
//don't override existing expression (style: #{entry.property}) - make a special mapping | |
List<ProcessedInformationEntry> furtherEntries = valueBindingConvertedValueMapping | |
.get(valueBindingExpression).getFurtherEntries(); | |
if (furtherEntries == null) | |
{ | |
furtherEntries = new ArrayList<ProcessedInformationEntry>(); | |
valueBindingConvertedValueMapping.get(valueBindingExpression) | |
.setFurtherEntries(furtherEntries); | |
} | |
furtherEntries.add(entry); | |
} | |
else | |
{ | |
//for normal validation | |
valueBindingConvertedValueMapping | |
.put(valueBindingExpression, entry); | |
} | |
} | |
public static String getBasePackage() | |
{ | |
return getInformationProviderBean().getBasePackage(); | |
} | |
public static InformationProviderBean getInformationProviderBean() | |
{ | |
Map applicationMap = FacesContext.getCurrentInstance() | |
.getExternalContext().getApplicationMap(); | |
InformationProviderBean bean = (InformationProviderBean) applicationMap | |
.get(InformationProviderBean.BEAN_NAME); | |
if (bean == null) | |
{ | |
return initInformationProviderBean(applicationMap); | |
} | |
return bean; | |
} | |
private static InformationProviderBean initInformationProviderBean( | |
Map applicationMap) | |
{ | |
List<String> informationProviderBeanClassNames = new ArrayList<String>(); | |
informationProviderBeanClassNames | |
.add(WebXmlParameter.CUSTOM_CONVENTION_INFO_PROVIDER_BEAN); | |
informationProviderBeanClassNames.add(ExtValUtils | |
.getCustomInformationProviderBeanClassName()); | |
informationProviderBeanClassNames.add(InformationProviderBean.class | |
.getName()); | |
InformationProviderBean informationProviderBean; | |
for (String className : informationProviderBeanClassNames) | |
{ | |
informationProviderBean = (InformationProviderBean) ClassUtils | |
.tryToInstantiateClassForName(className); | |
if (informationProviderBean != null) | |
{ | |
applicationMap.put(InformationProviderBean.BEAN_NAME, | |
informationProviderBean); | |
return informationProviderBean; | |
} | |
} | |
throw new IllegalStateException(InformationProviderBean.class.getName() | |
+ " not found"); | |
} | |
public static String getCustomInformationProviderBeanClassName() | |
{ | |
InformationProviderBean bean = (InformationProviderBean) ELUtils | |
.getBean(InformationProviderBean.CUSTOM_BEAN); | |
return (bean != null) ? bean.getClass().getName() : null; | |
} | |
public static void deregisterPhaseListener(PhaseListener phaseListener) | |
{ | |
LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder | |
.getFactory(FactoryFinder.LIFECYCLE_FACTORY); | |
String currentId; | |
Lifecycle currentLifecycle; | |
Iterator lifecycleIds = lifecycleFactory.getLifecycleIds(); | |
while (lifecycleIds.hasNext()) | |
{ | |
currentId = (String) lifecycleIds.next(); | |
currentLifecycle = lifecycleFactory.getLifecycle(currentId); | |
currentLifecycle.removePhaseListener(phaseListener); | |
} | |
} | |
public static final String VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY = ExtValUtils.class | |
.getName(); | |
public static Map<String, ProcessedInformationEntry> getOrInitValueBindingConvertedValueMapping() | |
{ | |
Map requestMap = FacesContext.getCurrentInstance().getExternalContext() | |
.getRequestMap(); | |
if (!requestMap.containsKey(VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY)) | |
{ | |
resetCrossValidationStorage(); | |
} | |
return (Map<String, ProcessedInformationEntry>) requestMap | |
.get(VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY); | |
} | |
public static void resetCrossValidationStorage() | |
{ | |
FacesContext.getCurrentInstance().getExternalContext().getRequestMap() | |
.put(VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY, | |
new HashMap<String, ProcessedInformationEntry>()); | |
} | |
/* | |
* workaround: mapping clientId -> proxy -> after restore view: find component + set converter of the mapping | |
* TODO: find a better solution - multi-window-mode | |
*/ | |
public static final String PROXY_MAPPING_KEY = VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY | |
+ ":proxyMapping"; | |
public static Map<String, Object> getOrInitProxyMapping() | |
{ | |
FacesContext facesContext = FacesContext.getCurrentInstance(); | |
//session scope is just the worst case - cleanup after restore view | |
Map sessionMap = facesContext.getExternalContext().getSessionMap(); | |
String viewId = facesContext.getViewRoot().getViewId(); | |
if (!sessionMap.containsKey(PROXY_MAPPING_KEY) | |
|| !((Map) sessionMap.get(PROXY_MAPPING_KEY)) | |
.containsKey(viewId)) | |
{ | |
resetProxyMapping(viewId); | |
} | |
return (Map<String, Object>) ((Map) sessionMap.get(PROXY_MAPPING_KEY)) | |
.get(viewId); | |
} | |
public static void resetProxyMapping(String viewId) | |
{ | |
Map sessionMap = FacesContext.getCurrentInstance().getExternalContext() | |
.getSessionMap(); | |
Map<String, Map<String, Object>> storage; | |
if (sessionMap.containsKey(PROXY_MAPPING_KEY)) | |
{ | |
storage = (Map) sessionMap.get(PROXY_MAPPING_KEY); | |
} | |
else | |
{ | |
storage = new HashMap<String, Map<String, Object>>(); | |
} | |
Map<String, Object> map; | |
if (!storage.containsKey(viewId)) | |
{ | |
map = new HashMap<String, Object>(); | |
storage.put(viewId, map); | |
} | |
sessionMap.put(PROXY_MAPPING_KEY, storage); | |
} | |
public static final String PROCESSED_CONVERTER_COUNT_KEY = VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY | |
+ ":processedConverterCount"; | |
public static Integer getProcessedConverterCount() | |
{ | |
Map requestMap = FacesContext.getCurrentInstance().getExternalContext() | |
.getRequestMap(); | |
if (!requestMap.containsKey(PROCESSED_CONVERTER_COUNT_KEY)) | |
{ | |
resetProcessedConverterMapping(); | |
} | |
return (Integer) requestMap.get(PROCESSED_CONVERTER_COUNT_KEY); | |
} | |
public static void setProcessedConverterCount(Integer count) | |
{ | |
Map requestMap = FacesContext.getCurrentInstance().getExternalContext() | |
.getRequestMap(); | |
if (!requestMap.containsKey(PROCESSED_CONVERTER_COUNT_KEY)) | |
{ | |
resetProcessedConverterMapping(); | |
} | |
requestMap.put(PROCESSED_CONVERTER_COUNT_KEY, count); | |
} | |
public static void resetProcessedConverterMapping() | |
{ | |
FacesContext.getCurrentInstance().getExternalContext().getRequestMap() | |
.put(PROCESSED_CONVERTER_COUNT_KEY, 0); | |
} | |
public static void increaseProcessedConverterCount() | |
{ | |
setProcessedConverterCount(getProcessedConverterCount() + 1); | |
} | |
public static void decreaseProcessedConverterCount() | |
{ | |
setProcessedConverterCount(getProcessedConverterCount() - 1); | |
} | |
public static boolean useProxyMapping() | |
{ | |
String initParam = WebXmlParameter.DEACTIVATE_PROXY_MAPPING; | |
boolean disableProxyMapping = (initParam != null && initParam.trim() | |
.equalsIgnoreCase("true")); | |
return !(useFallbackAdapters() || disableProxyMapping); | |
} | |
@Deprecated | |
public static boolean useFallbackAdapters() | |
{ | |
String initParam = WebXmlParameter.USE_ADAPTERS; | |
return (initParam != null && initParam.trim().equalsIgnoreCase("true")); | |
} | |
public static void restoreProxies() | |
{ | |
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot(); | |
if (viewRoot != null && ExtValUtils.useProxyMapping()) | |
{ | |
Map componentConverterMapping = ExtValUtils.getOrInitProxyMapping(); | |
Iterator current = componentConverterMapping.keySet().iterator(); | |
String key; | |
Converter converter; | |
Converter converterOfComponent; | |
UIComponent component = null; | |
while (current.hasNext()) | |
{ | |
key = (String) current.next(); | |
converter = (Converter) componentConverterMapping.get(key); | |
try | |
{ | |
component = viewRoot.findComponent(key); | |
} | |
catch (IllegalArgumentException e) | |
{ | |
//do nothing - it's just a ri bug with complex components - | |
//resolveComponentInComplexComponent will return the correct component | |
} | |
if (component == null) | |
{ | |
component = resolveComponentInComplexComponent(viewRoot, | |
component, key); | |
if (component == null) | |
{ | |
continue; | |
} | |
} | |
if (!(component instanceof ValueHolder)) | |
{ | |
continue; | |
} | |
converterOfComponent = ((ValueHolder) component).getConverter(); | |
//converterOfComponent lost callback during state-saving -> set converter of same type | |
if (converterOfComponent != null | |
&& converterOfComponent.getClass().getSuperclass() | |
.equals(converter.getClass().getSuperclass())) | |
{ | |
((ValueHolder) component).setConverter(converter); | |
} | |
} | |
} | |
if (ExtValUtils.useProxyMapping()) | |
{ | |
ExtValUtils.resetProxyMapping(FacesContext.getCurrentInstance() | |
.getViewRoot().getViewId()); | |
} | |
} | |
//TODO | |
private static UIComponent resolveComponentInComplexComponent( | |
UIComponent viewRoot, UIComponent component, String key) | |
{ | |
int index = key.lastIndexOf(":"); | |
if (index == -1) | |
{ | |
return null; | |
} | |
String newKey = key.substring(0, index); | |
if (viewRoot.findComponent(newKey) == null) | |
{ | |
int newIndex = newKey.lastIndexOf(":"); | |
if (newIndex < 1) | |
{ | |
return null; | |
} | |
newKey = newKey.substring(0, newIndex); | |
component = viewRoot.findComponent(newKey); | |
if (component == null) | |
{ | |
return null; | |
} | |
else | |
{ | |
return tryToResolveChildComponent(component, key.substring(key | |
.lastIndexOf(":"))); | |
} | |
} | |
return null; | |
} | |
//TODO | |
private static UIComponent tryToResolveChildComponent( | |
UIComponent component, String endOfKey) | |
{ | |
FacesContext facesContext = FacesContext.getCurrentInstance(); | |
String clientId = component.getClientId(facesContext); | |
if (clientId.contains(":") && clientId.substring(clientId.lastIndexOf(":")).endsWith(endOfKey)) | |
{ | |
return component; | |
} | |
UIComponent foundComponent; | |
for (UIComponent child : (List<UIComponent>) component.getChildren()) | |
{ | |
foundComponent = tryToResolveChildComponent(child, endOfKey); | |
if (foundComponent != null) | |
{ | |
return foundComponent; | |
} | |
} | |
return null; | |
} | |
public static final String ORIGINAL_APPLICATION_KEY = VALUE_BINDING_CONVERTED_VALUE_MAPPING_KEY | |
+ ":wrapped_application"; | |
//in order to access the wrapped application and support other Application wrappers | |
public static void setOriginalApplication(Application application) | |
{ | |
FacesContext facesContext = FacesContext.getCurrentInstance(); | |
Map applicationMap = facesContext.getExternalContext() | |
.getApplicationMap(); | |
if (!applicationMap.containsKey(ORIGINAL_APPLICATION_KEY)) | |
{ | |
synchronized (ExtValUtils.class) | |
{ | |
applicationMap.put(ORIGINAL_APPLICATION_KEY, application); | |
if (LOGGER.isTraceEnabled()) | |
{ | |
LOGGER.trace("the original application is " + application.getClass().getName()); | |
} | |
} | |
} | |
} | |
public static Application getOriginalApplication() | |
{ | |
return (Application) FacesContext.getCurrentInstance() | |
.getExternalContext().getApplicationMap().get( | |
ORIGINAL_APPLICATION_KEY); | |
} | |
public static Converter tryToCreateOriginalConverter( | |
FacesContext facesContext, UIComponent uiComponent) | |
{ | |
//for backward compatibility: cross-validation workaround with hidden field and static value | |
Class valueBindingType = ELUtils.getTypeOfValueBindingForComponent( | |
facesContext, uiComponent); | |
if (valueBindingType == null) | |
{ | |
return null; | |
} | |
return getOriginalApplication().createConverter(valueBindingType); | |
} | |
} |