diff options
author | Lukas Rytz <lukas.rytz@epfl.ch> | 2009-06-07 11:13:47 +0000 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@epfl.ch> | 2009-06-07 11:13:47 +0000 |
commit | 8cc477f8b6fd81c45fe30ac454c021a9769911ad (patch) | |
tree | ce574d8e84ea47c453298fe557b49f7d51ddfdf3 /src | |
parent | a2166dec9d687347a08c9e6a95e07f1a67f0d370 (diff) | |
download | scala-8cc477f8b6fd81c45fe30ac454c021a9769911ad.tar.gz scala-8cc477f8b6fd81c45fe30ac454c021a9769911ad.tar.bz2 scala-8cc477f8b6fd81c45fe30ac454c021a9769911ad.zip |
fixed BeanProperty, added BooleanBeanProperty, ...
fixed BeanProperty, added BooleanBeanProperty, added many tests (#1029,
#1751, #294, #1942, #1782, #1788, #637).
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Definitions.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/StdNames.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 128 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 31 | ||||
-rw-r--r-- | src/library/scala/reflect/BeanProperty.scala | 13 | ||||
-rw-r--r-- | src/library/scala/reflect/BooleanBeanProperty.scala | 21 |
6 files changed, 113 insertions, 84 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index b28cfc859c..ad0efdec11 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -346,6 +346,7 @@ trait Definitions { lazy val SerializableAttr: Symbol = getClass("scala.serializable") lazy val DeprecatedAttr: Symbol = getClass("scala.deprecated") lazy val BeanPropertyAttr: Symbol = getClass(sn.BeanProperty) + lazy val BooleanBeanPropertyAttr: Symbol = getClass(sn.BooleanBeanProperty) var AnnotationDefaultAttr: Symbol = _ lazy val NativeAttr: Symbol = getClass("scala.native") lazy val VolatileAttr: Symbol = getClass("scala.volatile") diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index ae2f3a39c7..0f60d2796f 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -418,6 +418,7 @@ trait StdNames { val ValueType : Name val Serializable : Name val BeanProperty : Name + val BooleanBeanProperty: Name val Delegate : Name val IOOBException: Name // IndexOutOfBoundsException val Code : Name @@ -465,6 +466,7 @@ trait StdNames { final val ValueType = newTermName("System.ValueType") final val Serializable = nme.NOSYMBOL final val BeanProperty = nme.NOSYMBOL + final val BooleanBeanProperty = nme.NOSYMBOL final val Delegate = newTermName("System.MulticastDelegate") final val IOOBException = newTermName("System.IndexOutOfRangeException") final val Code = nme.NOSYMBOL @@ -486,6 +488,7 @@ trait StdNames { private class J2SENames extends JavaNames { final val Serializable = newTermName("java.io.Serializable") final val BeanProperty = newTermName("scala.reflect.BeanProperty") + final val BooleanBeanProperty = newTermName("scala.reflect.BooleanBeanProperty") final val Code = newTermName("scala.reflect.Code") } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index ef2fa32118..157263d9cc 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -372,18 +372,13 @@ trait Namers { self: Analyzer => else mods.flags & ~PRESUPER | STABLE) if (nme.isSetterName(name)) context.error(tree.pos, "Names of vals or vars may not end in `_='") - var getter = owner.newMethod(tree.pos, name).setFlag(accflags) - setPrivateWithin(tree, getter, mods) - getter = enterInScope(getter).asInstanceOf[TermSymbol] - // needs the current context for adding synthetic BeanGetter / -Setter. - // namerOf(getter) has a primaryConstructorContext which cannot be used - // do add definitions to the class - setInfo(getter)(namerOf(getter).getterTypeCompleter(vd, context)) + // .isInstanceOf[..]: probably for (old) IDE hook. is this obsolete? + val getter = enterNewMethod(tree, name, accflags, mods).asInstanceOf[TermSymbol] + setInfo(getter)(namerOf(getter).getterTypeCompleter(vd)) if ((mods.flags & MUTABLE) != 0) { - var setter = owner.newMethod(tree.pos, nme.getterToSetter(name)) - .setFlag(accflags & ~STABLE & ~CASEACCESSOR) - setPrivateWithin(tree, setter, mods) - setter = enterInScope(setter).asInstanceOf[TermSymbol] + val setter = enterNewMethod(tree, nme.getterToSetter(name), + accflags & ~STABLE & ~CASEACCESSOR, + mods).asInstanceOf[TermSymbol] setInfo(setter)(namerOf(setter).setterTypeCompleter(vd)) } tree.symbol = @@ -403,6 +398,7 @@ trait Namers { self: Analyzer => vsym.setLazyAccessor(getter) vsym } else getter + addBeanGetterSetter(vd, getter) } case DefDef(mods, nme.CONSTRUCTOR, tparams, _, _, _) => var sym = owner.newConstructor(tree.pos).setFlag(mods.flags | owner.getFlag(ConstrFlags)) @@ -410,9 +406,7 @@ trait Namers { self: Analyzer => tree.symbol = enterInScope(sym) finishWith(tparams) case DefDef(mods, name, tparams, _, _, _) => - var sym = (owner.newMethod(tree.pos, name)).setFlag(mods.flags) - setPrivateWithin(tree, sym, mods) - tree.symbol = enterInScope(sym) + tree.symbol = enterNewMethod(tree, name, mods.flags, mods) finishWith(tparams) case TypeDef(mods, name, tparams, _) => var flags: Long = mods.flags @@ -444,6 +438,58 @@ trait Namers { self: Analyzer => tree.symbol } + def enterNewMethod(tree: Tree, name: Name, flags: Long, mods: Modifiers) = { + val sym = context.owner.newMethod(tree.pos, name).setFlag(flags) + setPrivateWithin(tree, sym, mods) + enterInScope(sym) + } + + private def addBeanGetterSetter(vd: ValDef, getter: Symbol) { + def isAnn(ann: Tree, demand: String) = ann match { + case Apply(Select(New(Ident(name)), _), _) => + name.toString == demand + case Apply(Select(New(Select(pre, name)), _), _) => + name.toString == demand + case _ => false + } + val ValDef(mods, name, tpt, _) = vd + val hasBP = mods.annotations.exists(isAnn(_, "BeanProperty")) + val hasBoolBP = mods.annotations.exists(isAnn(_, "BooleanBeanProperty")) + if ((hasBP || hasBoolBP) && !forMSIL) { + if (!name(0).isLetter) + context.error(vd.pos, "`BeanProperty' annotation can be applied "+ + "only to fields that start with a letter") + else if (mods hasFlag PRIVATE) + // avoids name clashes with private fields in traits + context.error(vd.pos, "`BeanProperty' annotation can only be applied "+ + "to non-private fields") + else { + val flags = (mods.flags & (DEFERRED | OVERRIDE | STATIC)) | SYNTHETIC + val beanName = name(0).toString.toUpperCase + name.subName(1, name.length) + + val getterName = if (hasBoolBP) "is" + beanName + else "get" + beanName + val getterMods = Modifiers(flags, mods.privateWithin, + mods.annotations map (_.duplicate)) + val beanGetterDef = atPos(vd.pos) { + DefDef(getterMods, getterName, Nil, List(Nil), tpt.duplicate, + if (mods hasFlag DEFERRED) EmptyTree + else Select(This(getter.owner.name), name)) } + enterSyntheticSym(beanGetterDef) + + if (mods hasFlag MUTABLE) { + // can't use "enterSyntheticSym", because the parameter type is not yet + // known. instead, uses the same machinery as for the non-bean setter: + // create and enter the symbol here, add the tree in Typer.addGettterSetter. + val setterName = "set" + beanName + val setter = enterNewMethod(vd, setterName, flags, mods).asInstanceOf[TermSymbol] + setInfo(setter)(namerOf(setter).setterTypeCompleter(vd)) + } + } + } + } + + // --- Lazy Type Assignment -------------------------------------------------- def typeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => @@ -478,12 +524,10 @@ trait Namers { self: Analyzer => } } - def getterTypeCompleter(vd: ValDef, getterCtx: Context) = mkTypeCompleter(vd) { sym => + def getterTypeCompleter(vd: ValDef) = mkTypeCompleter(vd) { sym => if (settings.debug.value) log("defining " + sym) val tp = typeSig(vd) sym.setInfo(PolyType(List(), tp)) - if (sym.hasAnnotation(BeanPropertyAttr) && sym.owner.isClass && !forMSIL) - addBeanGetterSetter(vd, tp, getterCtx) if (settings.debug.value) log("defined " + sym) validate(sym) } @@ -496,56 +540,6 @@ trait Namers { self: Analyzer => validate(sym) } - private def addBeanGetterSetter(vd: ValDef, tp: Type, getterCtx: Context) { - val ValDef(mods, name, _, _) = vd - val sym = vd.symbol - if (!name(0).isLetter) - context.error(sym.pos, "`BeanProperty' annotation can be applied "+ - "only to fields that start with a letter") - else { - val tmplCtx = getterCtx.nextEnclosing(c => c.scope.toList.contains(sym)) - assert(tmplCtx != NoContext, context) - val tmplNamer = newNamer(tmplCtx) - val flags = (mods.flags & (DEFERRED | OVERRIDE | STATIC)) | SYNTHETIC - val beanName = name(0).toString.toUpperCase + name.subName(1, name.length) - val getterName = if (tp == BooleanClass.tpe) "is" + beanName - else "get" + beanName - val existingGetter = sym.owner.info.decl(getterName) - if (existingGetter != NoSymbol) { - if (!existingGetter.hasFlag(SYNTHETIC)) - context.error(sym.pos, "a defintion of `"+ getterName + - "' already exists in "+ sym.owner) - } else { - val getterMods = Modifiers(flags, mods.privateWithin, - mods.annotations map (_.duplicate)) - val beanGetterDef = atPos(sym.pos) { - DefDef(getterMods, getterName, Nil, List(Nil), TypeTree(tp), - if (mods hasFlag DEFERRED) EmptyTree - else Select(This(sym.owner.name), name)) } - tmplNamer.enterSyntheticSym(beanGetterDef) - } - if (mods hasFlag MUTABLE) { - val setterName = "set" + beanName - val existingSetter = sym.owner.info.decl(setterName) - if (existingSetter != NoSymbol) { - if (!existingSetter.hasFlag(SYNTHETIC)) - context.error(sym.pos, "a defintion of `"+ setterName + - "' already exists in "+ sym.owner) - } else { - val setterMods = Modifiers(flags, mods.privateWithin, - mods.annotations map (_.duplicate)) - val param = ValDef(NoMods, "new" + name, TypeTree(tp), EmptyTree) - val beanSetterDef = atPos(sym.pos) { - DefDef(setterMods, setterName, Nil, List(List(param)), - TypeTree(UnitClass.tpe), - if (mods hasFlag DEFERRED) EmptyTree - else Assign(Select(This(sym.owner.name), name), Ident(param.name))) } - tmplNamer.enterSyntheticSym(beanSetterDef) - } - } - } - } - def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => var selftpe = typer.typedType(tree).tpe if (!(selftpe.typeSymbol isNonBottomSubClass sym.owner)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 89cfcb21e2..4255fd54e2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1271,22 +1271,33 @@ trait Typers { self: Analyzer => treeCopy.DefDef(result, result.mods, result.name, result.tparams, result.vparamss, result.tpt, result.rhs) } - def setterDef: DefDef = { - val setr = getter.setter(value.owner) - setr.setAnnotations(value.annotations) + def setterDef(setter: Symbol): DefDef = { + setter.setAnnotations(value.annotations) val result = atPos(vdef)( - DefDef(setr, - if ((mods hasFlag DEFERRED) || (setr hasFlag OVERLOADED)) + DefDef(setter, + if ((mods hasFlag DEFERRED) || (setter hasFlag OVERLOADED)) EmptyTree else typed(Assign(Select(This(value.owner), value), - Ident(setr.paramss.head.head))))) + Ident(setter.paramss.head.head))))) treeCopy.DefDef(result, result.mods, result.name, result.tparams, result.vparamss, result.tpt, result.rhs) } - val gs = if (mods hasFlag MUTABLE) List(getterDef, setterDef) - else List(getterDef) - if (mods hasFlag DEFERRED) gs else vdef :: gs + + val gs = new ListBuffer[DefDef] + gs.append(getterDef) + if (mods hasFlag MUTABLE) { + val setter = getter.setter(value.owner) + gs.append(setterDef(setter)) + if (!forMSIL && (value.hasAnnotation(BeanPropertyAttr) || + value.hasAnnotation(BooleanBeanPropertyAttr))) { + val beanSetterName = "set" + name(0).toString.toUpperCase + + name.subName(1, name.length) + val beanSetter = value.owner.info.decl(beanSetterName) + gs.append(setterDef(beanSetter)) + } + } + if (mods hasFlag DEFERRED) gs.toList else vdef :: gs.toList } case DocDef(comment, defn) => addGetterSetter(defn) map (stat => DocDef(comment, stat)) @@ -1798,7 +1809,7 @@ trait Typers { self: Analyzer => context.unit.synthetics get e.sym match { case Some(tree) => newStats += typedStat(tree) // might add even more synthetics to the scope - context.unit.synthetics -= e.sym + context.unit.synthetics -= e.sym case _ => } diff --git a/src/library/scala/reflect/BeanProperty.scala b/src/library/scala/reflect/BeanProperty.scala index 1c75cb02fc..50c095486f 100644 --- a/src/library/scala/reflect/BeanProperty.scala +++ b/src/library/scala/reflect/BeanProperty.scala @@ -12,22 +12,21 @@ package scala.reflect /** <p> - * This annotation adds a setter and a getter method, following the - * Java Bean convention (first letter of the property is capitalized) - * used by popular Java web frameworks. For example: + * When attached to a field, this annotation adds a setter and a getter + * method following the Java Bean convention. For example: * </p><pre> * @BeanProperty * <b>var</b> status = ""</pre> * <p> - * adds the following methods to the <b>generated</b> code + * adds the following methods to the class: * </p><pre> * <b>def</b> setStatus(s: String) { <b>this</b>.status = s } * <b>def</b> getStatus: String = <b>this</b>.status * </pre> * <p> - * However, you cannot call <code>setStatus</code> from - * <a href="http://scala-lang.org/" target="_top">Scala</a>, - * you should use the normal Scala access and assignment. + * For fields of type <code>Boolean</code>, if you need a getter + * named <code>isStatus</code>, use the + * <code>scala.reflect.BooleanBeanProperty</code> annotation instead. * </p> */ class BeanProperty extends StaticAnnotation diff --git a/src/library/scala/reflect/BooleanBeanProperty.scala b/src/library/scala/reflect/BooleanBeanProperty.scala new file mode 100644 index 0000000000..cd9f2e5e9e --- /dev/null +++ b/src/library/scala/reflect/BooleanBeanProperty.scala @@ -0,0 +1,21 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: BeanProperty.scala 17925 2009-05-30 18:10:21Z rytz $ + + +package scala.reflect + +/** <p> + * This annotation has the same functionality as + * <code>scala.reflect.BeanProperty</code>, but the generated + * Bean getter will be named <code>isFieldName</code> instead + * of <code>getFieldName</code>. + * </p> + */ +class BooleanBeanProperty extends StaticAnnotation |