blob: 83454efc0802804582f3e4fa32c84fa3303f5c45 [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.bval.xml;
import static org.apache.bval.model.Features.Property.JAVASCRIPT_VALIDATION_FUNCTIONS;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.bval.MetaBeanFactory;
import org.apache.bval.model.FeaturesCapable;
import org.apache.bval.model.MetaBean;
import org.apache.bval.model.MetaProperty;
import org.apache.bval.routines.StandardValidation;
/**
* Description: Create or enrich MetaBeans from apache beanInfos xml<br/>
*/
public class XMLMetaBeanFactory implements MetaBeanFactory {
private static final Logger logger = Logger.getLogger(XMLMetaBeanFactory.class.getName());
// use LinkedHashMap to keep sequence of loaders
private final Map<XMLMetaBeanLoader, XMLMetaBeanInfos> resources =
new LinkedHashMap<XMLMetaBeanLoader, XMLMetaBeanInfos>();
private StandardValidation standardValidation = StandardValidation.getInstance();
public interface Visitor {
/**
* @param xmlMeta - null or the bean found
* @param xmlInfos - all infos in a single unit (xml file)
* @throws Exception
*/
void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos) throws Exception;
MetaBean getMetaBean();
}
public static class XMLResult {
public XMLMetaBean xmlMeta;
public XMLMetaBeanInfos xmlInfos;
public XMLResult(XMLMetaBean metaBean, XMLMetaBeanInfos metaInfos) {
this.xmlMeta = metaBean;
this.xmlInfos = metaInfos;
}
public XMLResult() {
}
}
public void buildMetaBean(final MetaBean metaBean) throws Exception {
if(metaBean.getId() == null) return;
visitXMLBeanMeta(metaBean.getId(), new Visitor() {
public void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos)
throws Exception {
enrichMetaBean(metaBean, new XMLResult(xmlMeta, xmlInfos));
}
public MetaBean getMetaBean() {
return metaBean;
}
});
}
/** XMLMetaBeanLoader are used to know "locations" where to get BeanInfos from. */
public Collection<XMLMetaBeanLoader> getLoaders() {
return resources.keySet();
}
public void addLoader(XMLMetaBeanLoader loader) {
resources.put(loader, null);
}
public StandardValidation getStandardValidation() {
return standardValidation;
}
/** customize the implementation of standardValidation for this builder. */
public void setStandardValidation(StandardValidation standardValidation) {
this.standardValidation = standardValidation;
}
public void enrichMetaBean(MetaBean meta, XMLResult result) throws Exception {
if (result.xmlMeta.getId() != null) {
meta.setId(result.xmlMeta.getId());
}
if (result.xmlMeta.getName() != null) {
meta.setName(result.xmlMeta.getName());
}
/* if (meta.getBeanClass() == null && result.xmlMeta.getImpl() != null) {
meta.setBeanClass(findLocalClass(result.xmlMeta.getImpl()));
}*/
result.xmlMeta.mergeFeaturesInto(meta);
enrichValidations(meta, result.xmlMeta, result, false);
if (result.xmlMeta.getProperties() != null) {
for (XMLMetaProperty xmlProp : result.xmlMeta.getProperties()) {
enrichElement(meta, xmlProp, result);
}
}
if (result.xmlMeta.getBeanRefs() != null) {
for (XMLMetaBeanReference xmlRef : result.xmlMeta.getBeanRefs()) {
enrichElement(meta, xmlRef, result);
}
}
}
@SuppressWarnings("deprecation")
protected void enrichValidations(FeaturesCapable prop, XMLFeaturesCapable xmlProp,
XMLResult result, boolean addStandard)
throws Exception {
if (xmlProp.getValidators() != null) {
// obsolete code? remove from here --->
String[] func = prop.getFeature(JAVASCRIPT_VALIDATION_FUNCTIONS);
List<String> jsValidators = new ArrayList<String>(
xmlProp.getValidators().size() + (func == null ? 0 : func.length));
if (func != null && func.length > 0) {
jsValidators.addAll(Arrays.asList(func));
} // <--- to here
boolean useStandard = prop instanceof MetaProperty;
for (XMLMetaValidatorReference valRef : xmlProp.getValidators()) {
if (standardValidation != null &&
valRef.getRefId().equals(standardValidation.getValidationId())) {
useStandard = false;
}
XMLMetaValidator validator =
result.xmlInfos.getValidator(valRef.getRefId());
if (validator != null) {
if (validator.getValidation() != null) {
prop.addValidation(validator.getValidation());
}
if (validator.getJsFunction() != null &&
!jsValidators.contains(validator.getJsFunction())) {
jsValidators.add(validator.getJsFunction());
}
}
}
if (!jsValidators.isEmpty()) {
prop.putFeature(JAVASCRIPT_VALIDATION_FUNCTIONS,
jsValidators.toArray(new String[jsValidators.size()]));
}
if (useStandard && standardValidation != null) {
if (!prop.hasValidation(standardValidation))
prop.addValidation(standardValidation);
}
} else if (addStandard && standardValidation != null &&
!prop.hasValidation(standardValidation)) {
prop.addValidation(standardValidation);
}
}
protected MetaProperty enrichElement(MetaBean meta, XMLMetaElement xmlProp,
XMLResult result) throws Exception {
MetaProperty prop = meta.getProperty(xmlProp.getName());
if (prop == null) {
prop = new MetaProperty();
prop.setName(xmlProp.getName());
meta.putProperty(xmlProp.getName(), prop);
}
xmlProp.mergeInto(prop);
enrichValidations(prop, xmlProp, result, true);
return prop;
}
public void visitXMLBeanMeta(String beanId, Visitor visitor) throws Exception {
for (Map.Entry<XMLMetaBeanLoader, XMLMetaBeanInfos> entry : resources
.entrySet()) {
if (entry.getValue() == null) {
// load when not already loaded
try {
entry.setValue(entry.getKey().load());
} catch (IOException e) {
handleLoadException(entry.getKey(), e);
}
}
if (entry.getValue() != null) { // search in loaded infos for the 'name'
if (beanId == null) {
visitor.visit(null, entry.getValue());
} else {
XMLMetaBean found = entry.getValue().getBean(beanId);
if (found != null) {
visitor.visit(found, entry.getValue());
}
}
}
}
}
/**
* find a bean by the bean-id (=bean.name)
*
* @return null or the bean found from the first loader that has it.
*/
protected XMLResult findXMLBeanMeta(String beanId) {
for (Map.Entry<XMLMetaBeanLoader, XMLMetaBeanInfos> entry : resources
.entrySet()) {
if (entry.getValue() == null) {
// load when not already loaded
try {
entry.setValue(entry.getKey().load());
} catch (IOException e) {
handleLoadException(entry.getKey(), e);
}
}
if (entry.getValue() != null) { // search in loaded infos for the 'name'
XMLMetaBean found = entry.getValue().getBean(beanId);
if (found != null) {
return new XMLResult(found, entry.getValue());
}
}
}
return null; // not found!
}
public void handleLoadException(Object loader, IOException e) {
logger.log(Level.SEVERE, String.format("Error loading %s", loader), e);
}
}