1 /** 2 * Copyright (c) 2008, SnakeYAML 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package org.yaml.snakeyaml.introspector; 15 16 import java.beans.PropertyDescriptor; 17 import java.lang.annotation.Annotation; 18 import java.lang.reflect.Method; 19 import java.lang.reflect.Type; 20 import java.util.List; 21 import org.yaml.snakeyaml.error.YAMLException; 22 import org.yaml.snakeyaml.util.ArrayUtils; 23 24 /** 25 * <p> 26 * A <code>MethodProperty</code> is a <code>Property</code> which is accessed through accessor 27 * methods (setX, getX). It is possible to have a <code>MethodProperty</code> which has only setter, 28 * only getter, or both. It is not possible to have a <code>MethodProperty</code> which has neither 29 * setter nor getter. 30 * </p> 31 */ 32 public class MethodProperty extends GenericProperty { 33 34 private final PropertyDescriptor property; 35 private final boolean readable; 36 private final boolean writable; 37 discoverGenericType(PropertyDescriptor property)38 private static Type discoverGenericType(PropertyDescriptor property) { 39 Method readMethod = property.getReadMethod(); 40 if (readMethod != null) { 41 return readMethod.getGenericReturnType(); 42 } 43 44 Method writeMethod = property.getWriteMethod(); 45 if (writeMethod != null) { 46 Type[] paramTypes = writeMethod.getGenericParameterTypes(); 47 if (paramTypes.length > 0) { 48 return paramTypes[0]; 49 } 50 } 51 /* 52 * This actually may happen if PropertyDescriptor is of type IndexedPropertyDescriptor and it 53 * has only IndexedGetter/Setter. ATM we simply skip type discovery. 54 */ 55 return null; 56 } 57 MethodProperty(PropertyDescriptor property)58 public MethodProperty(PropertyDescriptor property) { 59 super(property.getName(), property.getPropertyType(), 60 MethodProperty.discoverGenericType(property)); 61 this.property = property; 62 this.readable = property.getReadMethod() != null; 63 this.writable = property.getWriteMethod() != null; 64 } 65 66 @Override set(Object object, Object value)67 public void set(Object object, Object value) throws Exception { 68 if (!writable) { 69 throw new YAMLException( 70 "No writable property '" + getName() + "' on class: " + object.getClass().getName()); 71 } 72 property.getWriteMethod().invoke(object, value); 73 } 74 75 @Override get(Object object)76 public Object get(Object object) { 77 try { 78 property.getReadMethod().setAccessible(true);// issue 50 79 return property.getReadMethod().invoke(object); 80 } catch (Exception e) { 81 throw new YAMLException("Unable to find getter for property '" + property.getName() 82 + "' on object " + object + ":" + e); 83 } 84 } 85 86 /** 87 * Returns the annotations that are present on read and write methods of this property or empty 88 * {@code List} if there're no annotations. 89 * 90 * @return the annotations that are present on this property or empty {@code List} if there're no 91 * annotations 92 */ 93 @Override getAnnotations()94 public List<Annotation> getAnnotations() { 95 List<Annotation> annotations; 96 if (isReadable() && isWritable()) { 97 annotations = ArrayUtils.toUnmodifiableCompositeList( 98 property.getReadMethod().getAnnotations(), property.getWriteMethod().getAnnotations()); 99 } else if (isReadable()) { 100 annotations = ArrayUtils.toUnmodifiableList(property.getReadMethod().getAnnotations()); 101 } else { 102 annotations = ArrayUtils.toUnmodifiableList(property.getWriteMethod().getAnnotations()); 103 } 104 return annotations; 105 } 106 107 /** 108 * Returns property's annotation for the given type or {@code null} if it's not present. If the 109 * annotation is present on both read and write methods, the annotation on read method takes 110 * precedence. 111 * 112 * @param annotationType the type of the annotation to be returned 113 * @return property's annotation for the given type or {@code null} if it's not present 114 */ 115 @Override getAnnotation(Class<A> annotationType)116 public <A extends Annotation> A getAnnotation(Class<A> annotationType) { 117 A annotation = null; 118 if (isReadable()) { 119 annotation = property.getReadMethod().getAnnotation(annotationType); 120 } 121 if (annotation == null && isWritable()) { 122 annotation = property.getWriteMethod().getAnnotation(annotationType); 123 } 124 return annotation; 125 } 126 127 @Override isWritable()128 public boolean isWritable() { 129 return writable; 130 } 131 132 @Override isReadable()133 public boolean isReadable() { 134 return readable; 135 } 136 137 } 138