blob: 4ca6442a63da14d9beceb469e6968e99adb0faa2 [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.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
}
}
}