Java tutorial
/* * Smart GWT (GWT for SmartClient) * Copyright 2008 and beyond, Isomorphic Software, Inc. * * Smart GWT is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 * is published by the Free Software Foundation. Smart GWT is also * available under typical commercial license terms - see * http://smartclient.com/license * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package com.smartgwt.rebind; import com.smartgwt.rebind.BeanMethod; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.user.rebind.SourceWriter; import com.smartgwt.rebind.BeanClass; import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Iterator; import java.util.regex.Pattern; import java.util.regex.Matcher; public class BeanProperty { private final String name; private final String nameWithoutOverload; private final LinkedList<BeanMethod> getters; private final LinkedList<BeanMethod> setters; private final BeanClass beanClass; private final static Pattern checkOverloadedName = Pattern.compile("^(.*)As[A-Z].*$"); public BeanProperty(String name, BeanClass beanClass) { this.name = name; this.beanClass = beanClass; Matcher matcher = checkOverloadedName.matcher(name); if (matcher.matches()) { nameWithoutOverload = matcher.group(1); } else { nameWithoutOverload = name; } getters = new LinkedList<BeanMethod>(); setters = new LinkedList<BeanMethod>(); } public String getName() { return name; } public String getNameWithBeanClass() { return beanClass.getSimpleBeanClassName() + "." + name; } public String getNameWithoutOverload() { return nameWithoutOverload; } // Checks whether this might be a "get...As..." property (e.g. // getWidthAsString), without looking for the main property. public boolean getMaybeOverloadedGetter() { // An overloaded getter property would have one getter and no setters, // since the convention would be to write the overloaded setter on the // main property, not the "As..." property. if (getters.size() != 1 || setters.size() != 0) return false; // An overloaded getter would have a different overloaded name. // Note that we can use object identity here. if (name == nameWithoutOverload) return false; // In principle, the As... part should be a reference to the return // type of the getter (e.g. AsString). But that won't necessarily be // true, so we don't check it. return true; } public BeanMethod firstGetter() { if (getters.size() == 0) { return null; } else { return getters.get(0); } } public void addMethod(BeanMethod method) { if (method == null) { return; } else if (method.isGetter()) { addGetter(method); } else if (method.isSetter()) { addSetter(method); } } public void addGetter(BeanMethod method) { if (method == null || !method.isGetter()) return; // If there is already a getter with the same type, we reject the // new getter, unless the current one is an "is..." and the new one // is a "get..." (e.g. isEnabled vs. getEnabled). In those cases, // the "get" version generally reflects the property of this // particular object, whereas the "is" version reflects more // dynamic factors, such as the containment hierarchy. // // With that exception, we prefer current getters with the same // type to new ones. Thus, as we merge methods from superclasses, // we will prefer the subclass method unless the superclass method // has a different type. (Which would only occur, for getters, in // the "get...As..." case.) // // We use the genericType so that boolean and Boolean will be // equal (etc.) BeanMethod existingGetter = getterForGenericType(method.getGenericType()); if (existingGetter != null) { if (existingGetter.getPrefix().equals("is")) { // If the existingGetter is an "is..." then remove it // and fall through to add the new one. getters.remove(existingGetter); beanClass.removeMethod(existingGetter); } else { // Otherwise, we prefer the existing getter, so we // return and don't add the new one. return; } } // Actually add the getter, both here and in the global list for the BeanClass getters.addLast(method); beanClass.addMethod(method); } public void addSetter(BeanMethod method) { if (method == null || !method.isSetter()) return; // For setters, we always prefer existing setters if the signature type is the same. BeanMethod existingSetter = setterForGenericType(method.getGenericType()); if (existingSetter == null) { setters.addLast(method); beanClass.addMethod(method); } } // Finds the getter with the specified signature type (where the signature // type equates boolean and Boolean etc. public BeanMethod getterForGenericType(JType type) { for (BeanMethod getter : getters) { if (getter.getGenericType() == type) return getter; } return null; } // Finds the setter with the specified signature type (where the signature // type equates boolean and Boolean etc. public BeanMethod setterForGenericType(JType type) { for (BeanMethod setter : setters) { if (setter.getGenericType() == type) return setter; } return null; } // Copy methods from another property into this property. Note that // addMethod will check for duplicate types and reject duplicates. Thus, // methods already on the property will be preferred to methods merged from // elsewhere (superclasses or alternate types). public void mergeProperty(BeanProperty otherProperty) { for (BeanMethod method : otherProperty.getters) { addMethod(method); } for (BeanMethod method : otherProperty.setters) { addMethod(method); } } public void writeConstructor(SourceWriter source, boolean addComma) { if (getters.size() == 0 && setters.size() == 0) { source.println("// Skipping because there are no getters and no setters: " + getName()); return; } if (getters.size() == 1 && setters.size() == 1 && setters.get(0).getValueType() == getters.get(0).getValueType()) { writeSingleConstructor(source, "BeanProperty1Getter1Setter", getters.get(0), setters.get(0), addComma); } else if (getters.size() == 1 && setters.size() == 0) { writeSingleConstructor(source, "BeanProperty1Getter", getters.get(0), null, addComma); } else if (setters.size() == 1 && getters.size() == 0) { writeSingleConstructor(source, "BeanProperty1Setter", null, setters.get(0), addComma); } else { writeMultipleConstructor(source, addComma); } } private int getMethodIndex(BeanMethod method) { return beanClass.indexOfMethod(method); } // This is called when there is at most one getter and one setter, and they // have the same type. Thus, it will be the usual case. public void writeSingleConstructor(SourceWriter source, String propertyType, BeanMethod getter, BeanMethod setter, boolean addComma) { final String beanClassName = beanClass.getSimpleBeanClassName(); source.println("new " + propertyType + "<" + beanClassName + "> (\"" + getName() + "\","); source.indent(); if (getter != null && setter != null) { getter.writeNew(source, beanClassName, getMethodIndex(getter), getMethodIndex(setter), false); } else if (getter != null) { getter.writeNew(source, beanClassName, getMethodIndex(getter), null, false); } else if (setter != null) { setter.writeNew(source, beanClassName, null, getMethodIndex(setter), false); } source.outdent(); source.println(")" + (addComma ? "," : "")); } public void writeMultipleConstructor(SourceWriter source, boolean addComma) { final String beanClassName = beanClass.getSimpleBeanClassName(); source.println("new BeanPropertyMultiple<" + beanClassName + "> (\"" + getName() + "\","); source.indent(); if (getters.size() == 0) { source.println("null, // no getters"); } else { source.println("new BeanMethod[] {"); source.indent(); Iterator<BeanMethod> iterator = getters.iterator(); while (iterator.hasNext()) { BeanMethod getter = iterator.next(); getter.writeNew(source, beanClassName, getMethodIndex(getter), null, iterator.hasNext()); } source.outdent(); source.println("},"); } if (setters.size() == 0) { source.println("null // no setters"); } else { source.println("new BeanMethod[] {"); source.indent(); Iterator<BeanMethod> iterator = setters.iterator(); while (iterator.hasNext()) { BeanMethod setter = iterator.next(); setter.writeNew(source, beanClassName, null, getMethodIndex(setter), iterator.hasNext()); } source.outdent(); source.println("}"); } source.outdent(); source.println(")" + (addComma ? "," : "")); } }