From e3a9494d58fd4162875cbfd5b08c98293e4d3002 Mon Sep 17 00:00:00 2001 From: tpylak <tpylak> Date: Tue, 30 Oct 2007 07:28:44 +0000 Subject: [PATCH] tab file loader for properties SVN: 2283 --- .../parser/AbstractParserObjectFactory.java | 32 +---- .../parser/AbstractPropertiesSetter.java | 131 ++++++++++++++++++ .../cisd/common/parser/IPropertiesSetter.java | 30 ++++ .../common/parser/IPropertyObjectFactory.java | 26 ++++ .../cisd/common/parser/ParseException.java | 6 - .../parser/PropertiesParserObjectFactory.java | 65 +++++++++ .../cisd/common/parser/TabFileLoader.java | 8 +- .../cisd/common/utilities/ClassUtils.java | 30 +++- 8 files changed, 287 insertions(+), 41 deletions(-) create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/AbstractPropertiesSetter.java create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/IPropertiesSetter.java create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/IPropertyObjectFactory.java create mode 100644 common/source/java/ch/systemsx/cisd/common/parser/PropertiesParserObjectFactory.java diff --git a/common/source/java/ch/systemsx/cisd/common/parser/AbstractParserObjectFactory.java b/common/source/java/ch/systemsx/cisd/common/parser/AbstractParserObjectFactory.java index 26eca52fb3b..6bb8ecb4c7f 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/AbstractParserObjectFactory.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/AbstractParserObjectFactory.java @@ -17,14 +17,11 @@ package ch.systemsx.cisd.common.parser; import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -51,8 +48,8 @@ public abstract class AbstractParserObjectFactory<E> implements IParserObjectFac */ private final Map<String, PropertyDescriptor> propertyDescriptors; - /** The list of mandatory <code>Field</code>s for {@link NewExperiment}, keyed by their name. */ - private final Map<String, Field> mandatoryFields; + /** The set of mandatory field names for {@link NewExperiment} */ + private final Set<String> mandatoryFields; /** The pool of {@link Converter}s. */ private final ConverterPool converterPool; @@ -65,7 +62,8 @@ public abstract class AbstractParserObjectFactory<E> implements IParserObjectFac assert beanClass != null; assert propertyMapper != null; propertyDescriptors = BeanUtils.getPropertyDescriptors(beanClass); - mandatoryFields = createMandatoryFields(beanClass); + mandatoryFields = ClassUtils.getMandatoryFields(beanClass); + assert mandatoryFields != null; checkPropertyMapper(beanClass, propertyMapper); this.propertyMapper = propertyMapper; converterPool = createConverterPool(); @@ -126,8 +124,8 @@ public abstract class AbstractParserObjectFactory<E> implements IParserObjectFac propertyNames.removeAll(propertyDescriptors.keySet()); if (propertyNames.size() > 0) { - throw UserFailureException.fromTemplate("The following header columns are not part of '%s': %s", - clazz.getSimpleName(), format(propertyNames)); + throw UserFailureException.fromTemplate("The following header columns are not part of '%s': %s", clazz + .getSimpleName(), format(propertyNames)); } } @@ -144,27 +142,11 @@ public abstract class AbstractParserObjectFactory<E> implements IParserObjectFac builder.setLength(builder.length() - 2); return builder.toString(); } - - /** - * Analyzes given <code>Class</code> and returns a <code>Map</code> containing the mandatory <code>Field</code>s - * keyed by {@link Field#getName()}. - */ - private final static Map<String, Field> createMandatoryFields(Class<?> clazz) - { - Map<String, Field> map = new HashMap<String, Field>(); - List<Field> fields = ClassUtils.getMandatoryFields(clazz); - for (Field field : fields) - { - map.put(field.getName(), field); - } - return map; - } /** Whether given field name is mandatory. */ private final boolean isMandatory(String fieldName) { - assert mandatoryFields != null; - return mandatoryFields.containsKey(fieldName); + return mandatoryFields.contains(fieldName); } private String getPropertyValue(final String[] lineTokens, final IPropertyModel propertyModel) diff --git a/common/source/java/ch/systemsx/cisd/common/parser/AbstractPropertiesSetter.java b/common/source/java/ch/systemsx/cisd/common/parser/AbstractPropertiesSetter.java new file mode 100644 index 00000000000..c63feb4165c --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/AbstractPropertiesSetter.java @@ -0,0 +1,131 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed 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 ch.systemsx.cisd.common.parser; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; +import ch.systemsx.cisd.common.utilities.ClassUtils; + +/** + * super class for constructing and holding key-value pairs + * + * @author Tomasz Pylak on Oct 26, 2007 + */ +abstract public class AbstractPropertiesSetter<ConstructedType> implements IPropertiesSetter<ConstructedType> +{ + abstract public ConstructedType done(); + + // --------------------- + + private final Map<String, String> properties; // map: property name -> valueOrNull + + private final Set<String> mandatoryFields; + + private final Set<String> availableProperties; + + protected AbstractPropertiesSetter(Class<?> beanClass) + { + this.properties = new HashMap<String, String>(); + this.mandatoryFields = toLowerCase(ClassUtils.getMandatoryFields(beanClass)); + this.availableProperties = toLowerCase(getAvailableProperties(beanClass)); + } + + private static Set<String> toLowerCase(Set<String> set) + { + Set<String> result = new HashSet<String>(); + for (String elem : set) + { + result.add(elem.toLowerCase()); + } + return result; + } + + public void setProperty(String name, String valueOrNull) + { + String propName = name.toLowerCase(); + Boolean mandatory = isMandatory(propName); + if (mandatory != null) + { + if (mandatory == true && valueOrNull == null) + { + throw createMandatoryException(propName); + } + properties.put(propName, valueOrNull); + } else + { + throw UserFailureException.fromTemplate("Unknown property name '%s', failed to set value to '%s'", + propName, valueOrNull); + } + } + + private static UserFailureException createMandatoryException(String name) + { + return UserFailureException.fromTemplate("Property '%s' is mandatory and cannot be set to the empty value.", + name); + } + + // temporary method, this information will be fetched from db + private Set<String> getAvailableProperties(Class<?> clazz) + { + Set<String> fieldNames = new HashSet<String>(); + for (Field field : clazz.getDeclaredFields()) + { + fieldNames.add(field.getName()); + } + Class<?> superclass = clazz.getSuperclass(); + if (superclass != null) + { + Set<String> superFieldNames = getAvailableProperties(superclass); + fieldNames.addAll(superFieldNames); + } + return fieldNames; + } + + /** return true if property is mandatory, false if it is optional and null if the property does not exist */ + private Boolean isMandatory(String name) + { + if (availableProperties.contains(name)) + { + return mandatoryFields.contains(name); + } else + { + return null; + } + } + + protected void checkMandatoryConstraint() + { + for (String name : availableProperties) + { + String value = properties.get(name); + if (mandatoryFields.contains(name) && value == null) + { + throw createMandatoryException(name); + } + } + } + + protected String tryGetValue(String name) + { + return properties.get(name.toLowerCase()); + } +} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/IPropertiesSetter.java b/common/source/java/ch/systemsx/cisd/common/parser/IPropertiesSetter.java new file mode 100644 index 00000000000..7d069e62539 --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/IPropertiesSetter.java @@ -0,0 +1,30 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed 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 ch.systemsx.cisd.common.parser; + +/** + * Used to construct object by setting its properties + * + * @author Tomasz Pylak on Oct 29, 2007 + */ +public interface IPropertiesSetter<ConstructedType> +{ + void setProperty(String name, String valueOrNull); + + /** returns constructed object, called after setting properties values */ + ConstructedType done(); +} \ No newline at end of file diff --git a/common/source/java/ch/systemsx/cisd/common/parser/IPropertyObjectFactory.java b/common/source/java/ch/systemsx/cisd/common/parser/IPropertyObjectFactory.java new file mode 100644 index 00000000000..fed3643981b --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/IPropertyObjectFactory.java @@ -0,0 +1,26 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed 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 ch.systemsx.cisd.common.parser; + +/** + * @author Tomasz Pylak on Oct 26, 2007 + */ +public interface IPropertyObjectFactory<E> +{ + /** @return properties setter which can be used to construct a new object */ + IPropertiesSetter<E> createObjectSetter(); +} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/ParseException.java b/common/source/java/ch/systemsx/cisd/common/parser/ParseException.java index 6af27a11dae..f8aa5ea362e 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/ParseException.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/ParseException.java @@ -30,12 +30,6 @@ public final class ParseException extends UserFailureException private final int lineNumber; - public ParseException(String message, int lineNumber) - { - super(message); - this.lineNumber = lineNumber; - } - public ParseException(String message, Throwable cause, int lineNumber) { super(message, cause); diff --git a/common/source/java/ch/systemsx/cisd/common/parser/PropertiesParserObjectFactory.java b/common/source/java/ch/systemsx/cisd/common/parser/PropertiesParserObjectFactory.java new file mode 100644 index 00000000000..2ba4ac359eb --- /dev/null +++ b/common/source/java/ch/systemsx/cisd/common/parser/PropertiesParserObjectFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2007 ETH Zuerich, CISD + * + * Licensed 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 ch.systemsx.cisd.common.parser; + +import java.util.Set; + +import ch.systemsx.cisd.common.exceptions.UserFailureException; + +/** + * Creates new object by setting its properties. + * + * @author Tomasz Pylak on Oct 26, 2007 + */ +public class PropertiesParserObjectFactory<E> implements IParserObjectFactory<E> +{ + /** The <code>IPropertyMapper</code> implementation. */ + private final IPropertyMapper propertyMapper; + + private final IPropertyObjectFactory<E> objectFactory; + + public PropertiesParserObjectFactory(IPropertyMapper propertyMapper, IPropertyObjectFactory<E> objectFactory) + { + this.propertyMapper = propertyMapper; + this.objectFactory = objectFactory; + } + + public E createObject(String[] lineTokens) throws UserFailureException + { + IPropertiesSetter<E> setter = objectFactory.createObjectSetter(); + Set<String> propertyNames = propertyMapper.getAllPropertyNames(); + for (String name : propertyNames) + { + final IPropertyModel propertyModel = propertyMapper.getProperty(name); + String propertyValue = getPropertyValue(lineTokens, propertyModel); + setter.setProperty(name, propertyValue); + } + return setter.done(); + } + + private String getPropertyValue(final String[] lineTokens, final IPropertyModel propertyModel) + { + int column = propertyModel.getColumn(); + if (column >= lineTokens.length) + { + throw UserFailureException.fromTemplate("Not enough tokens are available (index: %d, available: %d)", + column, lineTokens.length); + } + return lineTokens[column]; + } + +} diff --git a/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java b/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java index 2e6698480ae..de522c599d5 100644 --- a/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java +++ b/common/source/java/ch/systemsx/cisd/common/parser/TabFileLoader.java @@ -29,12 +29,12 @@ import ch.systemsx.cisd.common.exceptions.UserFailureException; /** * Convenient class to load a tab file and deliver a list of beans of type <code>T</code>. - * + * * @author Franz-Josef Elmer */ public class TabFileLoader<T> { - + private final IParserObjectFactoryFactory<T> factory; /** @@ -49,8 +49,8 @@ public class TabFileLoader<T> /** * Loads from the specified tab file a list of objects of type <code>T</code>. * - * @throws UserFailureException if the file does not exists, the header line with correct column - * bean attribute names is missing, or a parsing error occurs. + * @throws UserFailureException if the file does not exists, the header line with correct column bean attribute + * names is missing, or a parsing error occurs. * @throws EnvironmentFailureException if a IOException occured. */ public List<T> load(File file) diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java b/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java index 625fde0bf8b..20f3240af63 100644 --- a/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java +++ b/common/source/java/ch/systemsx/cisd/common/utilities/ClassUtils.java @@ -21,8 +21,10 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Properties; +import java.util.Set; import ch.systemsx.cisd.common.annotation.Mandatory; import ch.systemsx.cisd.common.exceptions.CheckedExceptionTunnel; @@ -40,10 +42,24 @@ public final class ClassUtils // Can not be instantiated. } + /** + * For given <code>Class</code> returns a set of field names that are annotated with {@link Mandatory}. + */ + public final static Set<String> getMandatoryFields(Class<?> clazz) + { + Set<String> set = new HashSet<String>(); + List<Field> fields = ClassUtils.getMandatoryFieldsList(clazz); + for (Field field : fields) + { + set.add(field.getName()); + } + return set; + } + /** * For given <code>Class</code> returns a list of fields that are annotated with {@link Mandatory}. */ - public final static List<Field> getMandatoryFields(Class<?> clazz) + private final static List<Field> getMandatoryFieldsList(Class<?> clazz) { return getMandatoryFields(clazz, null); } @@ -142,7 +158,7 @@ public final class ClassUtils { assert superClazz != null : "Missing super class"; assert className != null : "Missing class name"; - + try { final Class<?> clazz = Class.forName(className); @@ -152,7 +168,8 @@ public final class ClassUtils { return createInstance(clazz); } - final Constructor<?> constructor = clazz.getConstructor(new Class[] {Properties.class}); + final Constructor<?> constructor = clazz.getConstructor(new Class[] + { Properties.class }); return createInstance(constructor, properties); } catch (Exception ex) { @@ -165,12 +182,13 @@ public final class ClassUtils { return (T) clazz.newInstance(); } - + @SuppressWarnings("unchecked") private static <T> T createInstance(final Constructor<?> constructor, Properties properties) throws InstantiationException, IllegalAccessException, InvocationTargetException { - return (T) constructor.newInstance(new Object[] {properties}); + return (T) constructor.newInstance(new Object[] + { properties }); } - + } -- GitLab