| /** |
| * Copyright (c) 2008, http://www.snakeyaml.org |
| * |
| * 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 org.yaml.snakeyaml; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.yaml.snakeyaml.error.YAMLException; |
| import org.yaml.snakeyaml.introspector.BeanAccess; |
| import org.yaml.snakeyaml.introspector.Property; |
| import org.yaml.snakeyaml.introspector.PropertySubstitute; |
| import org.yaml.snakeyaml.introspector.PropertyUtils; |
| import org.yaml.snakeyaml.nodes.Node; |
| import org.yaml.snakeyaml.nodes.Tag; |
| |
| /** |
| * Provides additional runtime information necessary to create a custom Java |
| * instance. |
| * |
| * In general this class is thread-safe and can be used as a singleton, the only |
| * exception being the PropertyUtils field. A singleton PropertyUtils should be |
| * constructed and shared between all YAML Constructors used if a singleton |
| * TypeDescription is used, since Constructor sets its propertyUtils to the |
| * TypeDescription that is passed to it, hence you may end up in a situation |
| * when propertyUtils in TypeDescription is from different Constructor. |
| */ |
| public class TypeDescription { |
| |
| private final Class<? extends Object> type; |
| |
| //class that implements the described type; if set, will be used as a source for constructor. If not set - TypeDescription will leave instantiation of an entity to the YAML Constructor |
| private Class<?> impl; |
| |
| private Tag tag; |
| |
| transient private Set<Property> dumpProperties; |
| transient private PropertyUtils propertyUtils; |
| transient private boolean delegatesChecked; |
| |
| private Map<String, PropertySubstitute> properties = Collections.emptyMap(); |
| |
| protected Set<String> excludes = Collections.emptySet(); |
| protected String[] includes = null; |
| protected BeanAccess beanAccess; |
| |
| public TypeDescription(Class<? extends Object> clazz, Tag tag) { |
| this(clazz, tag, null); |
| } |
| |
| public TypeDescription(Class<? extends Object> clazz, Tag tag, Class<?> impl) { |
| this.type = clazz; |
| this.tag = tag; |
| this.impl = impl; |
| beanAccess = null; |
| } |
| |
| public TypeDescription(Class<? extends Object> clazz, String tag) { |
| this(clazz, new Tag(tag), null); |
| } |
| |
| public TypeDescription(Class<? extends Object> clazz) { |
| this(clazz, (Tag) null, null); |
| } |
| |
| public TypeDescription(Class<? extends Object> clazz, Class<?> impl) { |
| this(clazz, null, impl); |
| } |
| |
| /** |
| * Get tag which shall be used to load or dump the type (class). |
| * |
| * @return tag to be used. It may be a tag for Language-Independent Types |
| * (http://www.yaml.org/type/) |
| */ |
| public Tag getTag() { |
| return tag; |
| } |
| |
| /** |
| * Set tag to be used to load or dump the type (class). |
| * |
| * @param tag |
| * local or global tag |
| */ |
| public void setTag(Tag tag) { |
| this.tag = tag; |
| } |
| |
| public void setTag(String tag) { |
| setTag(new Tag(tag)); |
| } |
| |
| /** |
| * Get represented type (class) |
| * |
| * @return type (class) to be described. |
| */ |
| public Class<? extends Object> getType() { |
| return type; |
| } |
| |
| /** |
| * Specify that the property is a type-safe <code>List</code>. |
| * |
| * @param property |
| * name of the JavaBean property |
| * @param type |
| * class of List values |
| */ |
| @Deprecated |
| public void putListPropertyType(String property, Class<? extends Object> type) { |
| addPropertyParameters(property, type); |
| } |
| |
| /** |
| * Get class of List values for provided JavaBean property. |
| * |
| * @param property |
| * property name |
| * @return class of List values |
| */ |
| @Deprecated |
| public Class<? extends Object> getListPropertyType(String property) { |
| if (properties.containsKey(property)) { |
| Class<?>[] typeArguments = properties.get(property).getActualTypeArguments(); |
| if (typeArguments != null && typeArguments.length > 0) { |
| return typeArguments[0]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Specify that the property is a type-safe <code>Map</code>. |
| * |
| * @param property |
| * property name of this JavaBean |
| * @param key |
| * class of keys in Map |
| * @param value |
| * class of values in Map |
| */ |
| @Deprecated |
| public void putMapPropertyType(String property, Class<? extends Object> key, |
| Class<? extends Object> value) { |
| addPropertyParameters(property, key, value); |
| } |
| |
| /** |
| * Get keys type info for this JavaBean |
| * |
| * @param property |
| * property name of this JavaBean |
| * @return class of keys in the Map |
| */ |
| @Deprecated |
| public Class<? extends Object> getMapKeyType(String property) { |
| if (properties.containsKey(property)) { |
| Class<?>[] typeArguments = properties.get(property).getActualTypeArguments(); |
| if (typeArguments != null && typeArguments.length > 0) { |
| return typeArguments[0]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Get values type info for this JavaBean |
| * |
| * @param property |
| * property name of this JavaBean |
| * @return class of values in the Map |
| */ |
| @Deprecated |
| public Class<? extends Object> getMapValueType(String property) { |
| if (properties.containsKey(property)) { |
| Class<?>[] typeArguments = properties.get(property).getActualTypeArguments(); |
| if (typeArguments != null && typeArguments.length > 1) { |
| return typeArguments[1]; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Adds new substitute for property <code>pName</code> parameterized by |
| * <code>classes</classes> to this <code>TypeDescription</code>. If |
| * <code>pName</code> has been added before - updates parameters with |
| * <code>classes</code>. |
| * |
| * @param pName |
| * @param classes |
| */ |
| public void addPropertyParameters(String pName, Class<?>... classes) { |
| if (!properties.containsKey(pName)) { |
| substituteProperty(pName, null, null, null, classes); |
| } else { |
| PropertySubstitute pr = properties.get(pName); |
| pr.setActualTypeArguments(classes); |
| } |
| |
| } |
| |
| @Override |
| public String toString() { |
| return "TypeDescription for " + getType() + " (tag='" + getTag() + "')"; |
| } |
| |
| private void checkDelegates() { |
| Collection<PropertySubstitute> values = properties.values(); |
| for (PropertySubstitute p : values) { |
| try { |
| p.setDelegate(discoverProperty(p.getName())); |
| } catch (YAMLException e) { |
| } |
| } |
| delegatesChecked = true; |
| } |
| |
| private Property discoverProperty(String name) { |
| if (propertyUtils != null) { |
| if (beanAccess == null) { |
| return propertyUtils.getProperty(type, name); |
| } |
| return propertyUtils.getProperty(type, name, beanAccess); |
| } |
| return null; |
| } |
| |
| public Property getProperty(String name) { |
| if (!delegatesChecked) { |
| checkDelegates(); |
| } |
| return properties.containsKey(name) ? properties.get(name) : discoverProperty(name); |
| } |
| |
| /** |
| * Adds property substitute for <code>pName</code> |
| * |
| * @param pName |
| * property name |
| * @param pType |
| * property type |
| * @param getter |
| * method name for getter |
| * @param setter |
| * method name for setter |
| * @param argParams |
| * actual types for parameterized type (List<?>, Map<?>) |
| */ |
| public void substituteProperty(String pName, Class<?> pType, String getter, String setter, |
| Class<?>... argParams) { |
| substituteProperty(new PropertySubstitute(pName, pType, getter, setter, argParams)); |
| } |
| |
| public void substituteProperty(PropertySubstitute substitute) { |
| if (Collections.EMPTY_MAP == properties) { |
| properties = new LinkedHashMap<String, PropertySubstitute>(); |
| } |
| substitute.setTargetType(type); |
| properties.put(substitute.getName(), substitute); |
| } |
| |
| public void setPropertyUtils(PropertyUtils propertyUtils) { |
| this.propertyUtils = propertyUtils; |
| } |
| |
| /* begin: Representer */ |
| public void setIncludes(String... propNames) { |
| this.includes = (propNames != null && propNames.length > 0) ? propNames : null; |
| } |
| |
| public void setExcludes(String... propNames) { |
| if (propNames != null && propNames.length > 0) { |
| excludes = new HashSet<String>(); |
| for (String name : propNames) { |
| excludes.add(name); |
| } |
| } else { |
| excludes = Collections.emptySet(); |
| } |
| } |
| |
| public Set<Property> getProperties() { |
| if (dumpProperties != null) { |
| return dumpProperties; |
| } |
| |
| if (propertyUtils != null) { |
| if (includes != null) { |
| dumpProperties = new LinkedHashSet<Property>(); |
| for (String propertyName : includes) { |
| if (!excludes.contains(propertyName)) { |
| dumpProperties.add(getProperty(propertyName)); |
| } |
| } |
| return dumpProperties; |
| } |
| |
| final Set<Property> readableProps = (beanAccess == null) ? propertyUtils |
| .getProperties(type) : propertyUtils.getProperties(type, beanAccess); |
| |
| if (properties.isEmpty()) { |
| if (excludes.isEmpty()) { |
| return dumpProperties = readableProps; |
| } |
| dumpProperties = new LinkedHashSet<Property>(); |
| for (Property property : readableProps) { |
| if (!excludes.contains(property.getName())) { |
| dumpProperties.add(property); |
| } |
| } |
| return dumpProperties; |
| } |
| |
| if (!delegatesChecked) { |
| checkDelegates(); |
| } |
| |
| dumpProperties = new LinkedHashSet<Property>(); |
| |
| for (Property property : properties.values()) { |
| if (!excludes.contains(property.getName()) && property.isReadable()) { |
| dumpProperties.add(property); |
| } |
| } |
| |
| for (Property property : readableProps) { |
| if (!excludes.contains(property.getName())) { |
| dumpProperties.add(property); |
| } |
| } |
| |
| return dumpProperties; |
| } |
| return null; |
| } |
| |
| /* end: Representer */ |
| |
| /*------------ Maybe something useful to override :) ---------*/ |
| |
| public boolean setupPropertyType(String key, Node valueNode) { |
| return false; |
| } |
| |
| public boolean setProperty(Object targetBean, String propertyName, Object value) |
| throws Exception { |
| return false; |
| } |
| |
| /** |
| * This method should be overriden for TypeDescription implementations that are supposed to implement instantiation logic that is different from default one as |
| * implemented in YAML constructors. |
| * Note that even if you override this method, default filling of fields with variables from parsed YAML will still occur later. |
| |
| * @param node |
| * @return |
| */ |
| public Object newInstance(Node node) { |
| if (impl != null) { |
| try { |
| java.lang.reflect.Constructor<?> c = impl.getDeclaredConstructor(); |
| c.setAccessible(true); |
| return c.newInstance(); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| impl = null; |
| } |
| } |
| return null; |
| } |
| |
| public Object newInstance(String propertyName, Node node) { |
| return null; |
| } |
| |
| /** |
| * Is invoked after entity is filled with values from deserialized YAML |
| * @param obj - deserialized entity |
| * @return postprocessed deserialized entity |
| */ |
| public Object finalizeConstruction(Object obj) { |
| return obj; |
| } |
| |
| } |