summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@epfl.ch>2009-06-07 11:13:47 +0000
committerLukas Rytz <lukas.rytz@epfl.ch>2009-06-07 11:13:47 +0000
commit8cc477f8b6fd81c45fe30ac454c021a9769911ad (patch)
treece574d8e84ea47c453298fe557b49f7d51ddfdf3 /src
parenta2166dec9d687347a08c9e6a95e07f1a67f0d370 (diff)
downloadscala-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.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala3
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala128
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala31
-rw-r--r--src/library/scala/reflect/BeanProperty.scala13
-rw-r--r--src/library/scala/reflect/BooleanBeanProperty.scala21
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