| /* |
| * 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.netbeans.modules.beans; |
| |
| import java.beans.IntrospectionException; |
| import java.beans.Introspector; |
| import java.util.List; |
| import javax.lang.model.element.ExecutableElement; |
| import javax.lang.model.element.VariableElement; |
| import javax.lang.model.type.ArrayType; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import org.netbeans.api.java.source.CompilationInfo; |
| |
| import static org.netbeans.modules.beans.BeanUtils.*; |
| |
| /** Contains classes good for analysis time. These objects have to be only |
| * inside the CancellableTask() |
| * |
| * @author phrebejk |
| */ |
| public class TmpPattern { |
| |
| |
| public static class Property { |
| |
| ExecutableElement getterMethod; |
| ExecutableElement setterMethod; |
| VariableElement estimatedField; |
| TypeMirror type; |
| String name; |
| |
| public Property( CompilationInfo javac, ExecutableElement getterMethod, |
| ExecutableElement setterMethod ) |
| throws IntrospectionException { |
| |
| this.getterMethod = getterMethod; |
| this.setterMethod = setterMethod; |
| |
| type = findPropertyType(javac); |
| name = findPropertyName(); |
| } |
| |
| /** Package private constructor. Merges two property descriptors. Where they |
| * conflict, gives the second argument (y) priority over the first argumnet (x). |
| * @param x The first (lower priority) PropertyPattern. |
| * @param y The second (higher priority) PropertyPattern. |
| */ |
| Property( CompilationInfo javac, Property x, Property y ) { |
| |
| // Figure out the merged getterMethod |
| ExecutableElement xr = x.getterMethod; |
| ExecutableElement yr = y.getterMethod; |
| getterMethod = xr; |
| |
| // Normaly give priority to y's getterMethod |
| if ( yr != null ) { |
| getterMethod = yr; |
| } |
| |
| // However, if both x and y reference read method in the same class, |
| // give priority to a boolean "is" method over boolean "get" method. |
| if ( xr != null && yr != null && |
| xr.getEnclosingElement() == yr.getEnclosingElement() && |
| xr.getReturnType().getKind() == TypeKind.BOOLEAN && |
| yr.getReturnType().getKind() == TypeKind.BOOLEAN && |
| nameAsString(xr).indexOf(IS_PREFIX) == 0 && |
| nameAsString(yr).indexOf(GET_PREFIX) == 0 ) { |
| getterMethod = xr; |
| } |
| |
| setterMethod = x.setterMethod; |
| if ( y.setterMethod != null ) { |
| setterMethod = y.setterMethod; |
| } |
| |
| // PENDING bound and constrained |
| /* |
| bound = x.bound | y.bound; |
| constrained = x.constrained | y.constrained |
| */ |
| |
| try { |
| type = findPropertyType(javac); |
| } |
| catch ( IntrospectionException ex ) { |
| //System.out.println (x.getName() + ":" + y.getName()); // NOI18N |
| //System.out.println (x.getType() + ":" + y.getType() ); // NOI18N |
| throw new IllegalStateException("Mixing invalid PropertyPattrens", ex); // NOI18N |
| } |
| |
| name = findPropertyName(); |
| } |
| |
| public PropertyPattern createPattern( PatternAnalyser analyser ) throws IntrospectionException { |
| return new PropertyPattern( analyser, getterMethod, setterMethod, estimatedField, type, name ); |
| } |
| |
| // Private methods ----------------------------------------------------- |
| |
| /** Resolves the type of the property from type of getter and setter. |
| * @throws IntrospectionException if the property doesnt folow the design patterns |
| * @return The type of the property. |
| */ |
| TypeMirror findPropertyType(CompilationInfo javac) throws IntrospectionException { |
| |
| TypeMirror resolvedType = null; |
| |
| if ( getterMethod != null ) { |
| if ( !getterMethod.getParameters().isEmpty() ) { |
| throw new IntrospectionException( "bad read method arg count" ); // NOI18N |
| } |
| resolvedType = getterMethod.getReturnType(); |
| if ( resolvedType.getKind() == TypeKind.VOID ) { |
| throw new IntrospectionException( "read method " + getterMethod.getSimpleName() + // NOI18N |
| " returns void" ); // NOI18N |
| } |
| } |
| |
| if ( setterMethod != null ) { |
| List<? extends VariableElement> params = setterMethod.getParameters(); |
| if ( params.size() != 1 ) { |
| throw new IntrospectionException( "bad write method arg count" ); // NOI18N |
| } |
| VariableElement param = params.get(0); |
| if ( resolvedType != null && !javac.getTypes().isSameType(resolvedType, param.asType()) ) { |
| throw new IntrospectionException( "type mismatch between read and write methods" ); // NOI18N |
| } |
| resolvedType = param.asType(); |
| } |
| return resolvedType; |
| } |
| |
| /** Based on names of getter and setter resolves the name of the property. |
| * @return Name of the property |
| */ |
| String findPropertyName() { |
| String methodName = null; |
| |
| if ( getterMethod != null ) |
| methodName = nameAsString(getterMethod); |
| else if ( setterMethod != null ) |
| methodName = nameAsString(setterMethod); |
| else { |
| return null; |
| } |
| |
| return methodName.startsWith( IS_PREFIX ) ? // NOI18N |
| Introspector.decapitalize( methodName.substring(2) ) : |
| Introspector.decapitalize( methodName.substring(3) ); |
| } |
| |
| } |
| |
| public static class IdxProperty extends Property { |
| |
| private ExecutableElement indexedGetterMethod; |
| private ExecutableElement indexedSetterMethod; |
| private TypeMirror indexedType; |
| |
| /** Creates new IndexedProperty just one of the methods indexedGetterMethod |
| * and indexedSetterMethod may be null. |
| * @param patternAnalyser patternAnalyser which creates this Property. |
| * @param getterMethod getterMethod may be <CODE>null</CODE>. |
| * @param setterMethod setterMethod may be <CODE>null</CODE>. |
| * @param indexedGetterMethod getterMethod of the property or <CODE>null</CODE>. |
| * @param indexedSetterMethod setterMethod of the property or <CODE>null</CODE>. |
| * @throws IntrospectionException If specified methods do not follow beans Property rules. |
| */ |
| public IdxProperty( CompilationInfo ci, |
| ExecutableElement getterMethod, ExecutableElement setterMethod, |
| ExecutableElement indexedGetterMethod, ExecutableElement indexedSetterMethod ) |
| throws IntrospectionException { |
| |
| super ( ci, getterMethod, setterMethod ); |
| |
| this.indexedGetterMethod = indexedGetterMethod; |
| this.indexedSetterMethod = indexedSetterMethod; |
| |
| indexedType = findIndexedPropertyType(ci); |
| |
| if (this.type == null && this.indexedType != null) { |
| this.type = ci.getTypes().getArrayType(this.indexedType); |
| } |
| |
| name = findIndexedPropertyName(); |
| } |
| |
| /** Package private constructor. Merges two property descriptors. Where they |
| * conflict, gives the second argument (y) priority over the first argumnet (x). |
| * @param x The first (lower priority) PropertyPattern. |
| * @param y The second (higher priority) PropertyPattern. |
| */ |
| IdxProperty( CompilationInfo javac, Property x, Property y ) { |
| super(javac, x, y); |
| if ( x instanceof IdxProperty ) { |
| IdxProperty ix = (IdxProperty)x; |
| indexedGetterMethod = ix.indexedGetterMethod; |
| indexedSetterMethod = ix.indexedSetterMethod; |
| indexedType = ix.indexedType; |
| type = type == null? ix.type: type; |
| } |
| if ( y instanceof IdxProperty ) { |
| IdxProperty iy = (IdxProperty)y; |
| if ( iy.indexedGetterMethod != null ) |
| indexedGetterMethod = iy.indexedGetterMethod; |
| if ( iy.indexedSetterMethod != null ) |
| indexedSetterMethod = iy.indexedSetterMethod; |
| indexedType = iy.indexedType; |
| type = type == null? iy.type: type; |
| } |
| name = findIndexedPropertyName(); |
| } |
| |
| |
| public IdxPropertyPattern createPattern( PatternAnalyser analyser) throws IntrospectionException { |
| return new IdxPropertyPattern(analyser, |
| getterMethod, setterMethod, |
| indexedGetterMethod, indexedSetterMethod, |
| estimatedField, |
| type, |
| indexedType, |
| name); |
| } |
| |
| |
| // Private methods ----------------------------------------------------- |
| |
| /** Resolves the indexed type of the property from type of getter and setter. |
| * Checks for conformance to Beans design patterns. |
| * @throws IntrospectionException if the property does not follow the design patterns |
| */ |
| private TypeMirror findIndexedPropertyType(CompilationInfo javac) throws IntrospectionException { |
| |
| indexedType = null; |
| |
| if ( indexedGetterMethod != null ) { |
| List<? extends VariableElement> params = indexedGetterMethod.getParameters(); |
| if ( params.size() != 1 ) { |
| throw new IntrospectionException( "bad indexed read method arg count" ); // NOI18N |
| } |
| VariableElement param = params.get(0); |
| if ( param.asType().getKind() != TypeKind.INT ) { |
| throw new IntrospectionException( "not int index to indexed read method" ); // NOI18N |
| } |
| indexedType = indexedGetterMethod.getReturnType(); |
| if ( indexedType.getKind() ==TypeKind.VOID ) { |
| throw new IntrospectionException( "indexed read method return void" ); // NOI18N |
| } |
| } |
| |
| if (indexedSetterMethod != null ) { |
| List<? extends VariableElement> params = indexedSetterMethod.getParameters(); |
| if ( params.size() != 2 ) { |
| throw new IntrospectionException( "bad indexed write method arg count" ); // NOI18N |
| } |
| VariableElement param1 = params.get(0); |
| if ( param1.asType().getKind() != TypeKind.INT ) { |
| throw new IntrospectionException( "non int index to indexed write method" ); // NOI18N |
| } |
| VariableElement param2 = params.get(1); |
| if (indexedType != null && !javac.getTypes().isSameType(indexedType, param2.asType()) ) { |
| throw new IntrospectionException( |
| "type mismatch between indexed read and write methods" ); // NOI18N |
| } |
| indexedType = param2.asType(); |
| } |
| |
| //type = indexedType; |
| |
| TypeMirror propType = type; |
| if ( propType != null && |
| ( (propType.getKind() != TypeKind.ARRAY) || !javac.getTypes().isSameType(indexedType, ((ArrayType)propType).getComponentType()))) { |
| throw new IntrospectionException( |
| "type mismatch between property type and indexed type" ); // NOI18N |
| } |
| return indexedType; |
| } |
| |
| /** Based on names of indexedGetter and indexedSetter resolves the name |
| * of the indexed property. |
| * @return Name of the indexed property |
| */ |
| String findIndexedPropertyName() { |
| String superName = findPropertyName(); |
| |
| if ( superName == null ) { |
| String methodName = null; |
| |
| if ( indexedGetterMethod != null ) |
| methodName = nameAsString(indexedGetterMethod); |
| else if ( indexedSetterMethod != null ) |
| methodName = nameAsString(indexedSetterMethod); |
| else |
| throw new InternalError( "Indexed property with all methods == null" ); // NOI18N |
| |
| return methodName.startsWith( IS_PREFIX ) ? // NOI18N |
| Introspector.decapitalize( methodName.substring(2) ) : |
| Introspector.decapitalize( methodName.substring(3) ); |
| } |
| else |
| return superName; |
| } |
| |
| } |
| |
| public static class EventSet { |
| |
| ExecutableElement addListenerMethod; |
| ExecutableElement removeListenerMethod; |
| boolean isUnicast; |
| TypeMirror type; |
| String name; |
| |
| public EventSet( CompilationInfo ci, |
| ExecutableElement addListenerMethod, |
| ExecutableElement removeListenerMethod ) { |
| |
| if ( addListenerMethod == null || removeListenerMethod == null ) |
| throw new NullPointerException(); |
| |
| this.addListenerMethod = addListenerMethod; |
| this.removeListenerMethod = removeListenerMethod; |
| |
| isUnicast = testUnicast( ci ); |
| type = findEventSetType(); |
| name = findEventSetName(); |
| } |
| |
| /* |
| * Package-private constructor |
| * Merge two event set descriptors. Where they conflict, give the |
| * second argument (y) priority over the first argument (x). |
| * |
| * @param x The first (lower priority) EventSetDescriptor |
| * @param y The second (higher priority) EventSetDescriptor |
| */ |
| EventSet( EventSet x, EventSet y) { |
| //super(x,y); |
| |
| /* |
| listenerMethodDescriptors = x.listenerMethodDescriptors; |
| if (y.listenerMethodDescriptors != null) { |
| listenerMethodDescriptors = y.listenerMethodDescriptors; |
| } |
| if (listenerMethodDescriptors == null) { |
| listenerMethods = y.listenerMethods; |
| } |
| */ |
| addListenerMethod = y.addListenerMethod; |
| removeListenerMethod = y.removeListenerMethod; |
| isUnicast = y.isUnicast; |
| type = y.type; |
| name = y.name; |
| |
| /* |
| if (!x.inDefaultEventSet || !y.inDefaultEventSet) { |
| inDefaultEventSet = false; |
| } |
| */ |
| } |
| |
| EventSetPattern createPattern( PatternAnalyser analyser) { |
| return new EventSetPattern( analyser, addListenerMethod, removeListenerMethod, name, type, isUnicast); |
| } |
| |
| // Private methods ----------------------------------------------------- |
| |
| /** Finds the Type of property.*/ |
| private TypeMirror findEventSetType() { |
| return addListenerMethod.getParameters().get(0).asType(); |
| } |
| |
| /** Decides about the name of the event set from names of the methods */ |
| private String findEventSetName() { |
| String compound = nameAsString(addListenerMethod).substring(3); |
| return name = Introspector.decapitalize( compound ); |
| } |
| |
| /** Test if this EventSet pattern is unicast */ |
| private boolean testUnicast( CompilationInfo ci ) { |
| return BeanUtils.isThrowing(ci, addListenerMethod, "java.util.TooManyListenersException"); // NOI18N |
| } |
| |
| } |
| |
| |
| |
| } |
| |