Some scala-swing ideas

View: New views
12 Messages — Rating Filter:   Alert me  

Some scala-swing ideas

by Naftoli Gugenheim :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

As part of an application I'm writing, I wrote some code, part of
which may be useful for scala-swing. It's not very polished, but I'm
copy-pasting some code that I wrote so more eyes will see it. Please
comment!

Components:

package chavrusa.frontend

import scala.swing._

import scala.collection.mutable.Map

import java.awt.{Font, Dimension}

/**
 * A component, with control of get/setComponentZOrder.
 * Uses OverlayLayout.
 */
trait ZOrdered extends Component with SequentialContainer.Wrapper {
  override lazy val peer: javax.swing.JPanel = {
    val p = new javax.swing.JPanel with SuperMixin
    p.setLayout(new javax.swing.OverlayLayout(p))
    p
  }
  lazy val zOrder: Map[Component, Int] = new Map[Component, Int] {
    def -=(c: Component) { _contents -= c }
    def update(c: Component, z: Int) = {
      peer.add(c.peer)
      peer.setComponentZOrder(c.peer, z)
    }
    def get(c: Component) = Some(peer.getComponentZOrder(c.peer))
    def size = contents.size
    def elements: Iterator[(Component, Int)] =
      contents.elements map {c => (c, apply(c))}
  }
  def toFront(c: Component) = zOrder(c) = 0
  def toBack(c: Component) = zOrder(c) = -1
}

/**
 * A JLayeredPane wrapper using OverlayLayout
 */
class OverlayLayeredPane extends SequentialContainer.Wrapper {
  override lazy val peer: javax.swing.JLayeredPane = {
    val lp = new javax.swing.JLayeredPane with SuperMixin
    lp.setLayout(new javax.swing.OverlayLayout(lp))
    lp
  }

  lazy val layer: Map[Component, (Int,Int)] = new Map[Component, (Int, Int)] {
    def -=(c: Component) { _contents -= c }
    def update(c: Component, i: (Int,Int)) = {
      i match {
        case (layer, pos) =>
          peer.setLayer(c.peer, layer)
          peer.add(c.peer)
          peer.setPosition(c.peer, pos)
      }
    }
    def get(c: Component) = Some((peer.getLayer(c.peer),
peer.getPosition(c.peer)))
    def size = contents.size
    def elements: Iterator[(Component, (Int,Int))] =
      contents.elements map {c => (c, apply(c))}
  }

  def toFront(c: Component) = {
    println(peer.getComponents contains c.peer)
    peer.moveToFront(c.peer)
  }

}


/**
 * Default settings for this particular kiosk.
 * Big font
 */
trait Defaults extends Component {
  xLayoutAlignment = 0.5
  font = new Font("SansSerif", java.awt.Font.PLAIN, 30)
}
/**
 * Big margins
 */
trait ButtonDefaults extends Button with Defaults {
  margin = new java.awt.Insets(5,5,5,5)
}

trait StretchFull extends Component {
  maximumSize = new Dimension(Math.MAX_INT, Math.MAX_INT)
}


/**
 * Adds a round red rectangle when not valid, and fires events
 */
trait ValidationDecorated extends Component with Publisher {
  case class Valid(source: Component) extends event.ComponentEvent
  case class Invalid(source: Component) extends event.ComponentEvent
  private var _valid = true
  def valid_=(v:Boolean) {
    val old = _valid
    _valid = v
//    if(old != v) {
      if(v) publish(Valid(this)) else publish(Invalid(this))
//    }
    repaint
  }
  def valid = _valid

  override def paint(g: java.awt.Graphics) = g match {
    case g: java.awt.Graphics2D =>
      super.paint(g)
      if(!valid) {
        g.setPaint(new java.awt.Color(1f,0f,0f,.75f))
        g.setStroke(new java.awt.BasicStroke(3f))
        g.drawRoundRect(0,0,size.width-1,size.height-1, 20,20)
      }
  }
}

/**
 * Wrapper for SwingX JXTitledPanel
 */
class TitledPanel extends Component {
  import org.jdesktop.swingx.JXTitledPanel
  override lazy val peer: JXTitledPanel = new JXTitledPanel

  def title = peer.getTitle
  def title_=(t: String) = peer.setTitle(t)

  def titleFont = peer.getTitleFont
  def titleFont_=(f: Font) = peer.setTitleFont(f)

  def content =
peer.getContentContainer.asInstanceOf[javax.swing.JComponent].getClientProperty("scala.swingWrapper").asInstanceOf[Component]
  def content_=(c: Component) = peer.setContentContainer(c.peer)
}






Effects: (take advantage of traits)
package chavrusa.frontend

import java.awt.Graphics2D
import java.awt.image.BufferedImage

import scala.swing._


trait EffectPainting extends Component {
  import java.awt.image.BufferedImage
  var paintingEffect = false
  var paintingBackground = true

  override def paint(g: java.awt.Graphics) = g match {
    case g: Graphics2D =>
      if(!paintingEffect) {
        super.paint(g)
      } else {
        val bi = new BufferedImage(size.width, size.height,
BufferedImage.TYPE_4BYTE_ABGR)
        val gBi = bi.createGraphics
        if(paintingBackground) {
          gBi.setPaint(background)
          gBi.fillRect(0,0,size.width,size.height)
        } /*else {
          gBi.clearRect(0,0,size.width,size.height)
        }*/
        super.paint(gBi)
        paintEffect(bi, g)
      }
  }
//  override def paintComponent(g: Graphics) {}

  def paintEffect(bi: BufferedImage, g: Graphics2D)
}
trait Translucent extends EffectPainting {
  private var _opacity = 1f
  def opacity = _opacity
  def opacity_=(v: Float) = {
    _opacity = v
    repaint
  }

  def paintEffect(bi: java.awt.image.BufferedImage, g: Graphics2D) {
    val oldComposite = g.getComposite
    g.setComposite(java.awt.AlphaComposite.getInstance(
      java.awt.AlphaComposite.SRC_OVER,
      Math.min(1,Math.max(0,opacity))
    ))
    g.drawImage(bi,0,0,null)
    g.setComposite(oldComposite)
  }
}

trait TransitionEffect extends EffectPainting {
  var initialImage: Option[BufferedImage] = None
  var transitionValue = 0f
}
trait FadeTransition extends TransitionEffect {
  def paintEffect(bi: BufferedImage, g0: Graphics2D) {
    val g = g0.create.asInstanceOf[Graphics2D]
    initialImage match {
      case Some(img) =>
        g.drawImage(img,0,0,null)
        g.setComposite(java.awt.AlphaComposite.getInstance(
          java.awt.AlphaComposite.SRC_OVER,
          Math.min(1,Math.max(0,transitionValue))
        ))
      case None =>
    }
    g.drawImage(bi,0,0,null)
  }
}

Re: Some scala-swing ideas

by Naftoli Gugenheim :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

No comments?

On Mon, Oct 5, 2009 at 3:28 PM, Naftoli Gugenheim <naftoligug@...> wrote:
As part of an application I'm writing, I wrote some code, part of
which may be useful for scala-swing. It's not very polished, but I'm
copy-pasting some code that I wrote so more eyes will see it. Please
comment!

Components:

package chavrusa.frontend

import scala.swing._

import scala.collection.mutable.Map

import java.awt.{Font, Dimension}

/**
 * A component, with control of get/setComponentZOrder.
 * Uses OverlayLayout.
 */
trait ZOrdered extends Component with SequentialContainer.Wrapper {
 override lazy val peer: javax.swing.JPanel = {
   val p = new javax.swing.JPanel with SuperMixin
   p.setLayout(new javax.swing.OverlayLayout(p))
   p
 }
 lazy val zOrder: Map[Component, Int] = new Map[Component, Int] {
   def -=(c: Component) { _contents -= c }
   def update(c: Component, z: Int) = {
     peer.add(c.peer)
     peer.setComponentZOrder(c.peer, z)
   }
   def get(c: Component) = Some(peer.getComponentZOrder(c.peer))
   def size = contents.size
   def elements: Iterator[(Component, Int)] =
     contents.elements map {c => (c, apply(c))}
 }
 def toFront(c: Component) = zOrder(c) = 0
 def toBack(c: Component) = zOrder(c) = -1
}

/**
 * A JLayeredPane wrapper using OverlayLayout
 */
class OverlayLayeredPane extends SequentialContainer.Wrapper {
 override lazy val peer: javax.swing.JLayeredPane = {
   val lp = new javax.swing.JLayeredPane with SuperMixin
   lp.setLayout(new javax.swing.OverlayLayout(lp))
   lp
 }

 lazy val layer: Map[Component, (Int,Int)] = new Map[Component, (Int, Int)] {
   def -=(c: Component) { _contents -= c }
   def update(c: Component, i: (Int,Int)) = {
     i match {
       case (layer, pos) =>
         peer.setLayer(c.peer, layer)
         peer.add(c.peer)
         peer.setPosition(c.peer, pos)
     }
   }
   def get(c: Component) = Some((peer.getLayer(c.peer),
peer.getPosition(c.peer)))
   def size = contents.size
   def elements: Iterator[(Component, (Int,Int))] =
     contents.elements map {c => (c, apply(c))}
 }

 def toFront(c: Component) = {
   println(peer.getComponents contains c.peer)
   peer.moveToFront(c.peer)
 }

}


/**
 * Default settings for this particular kiosk.
 * Big font
 */
trait Defaults extends Component {
 xLayoutAlignment = 0.5
 font = new Font("SansSerif", java.awt.Font.PLAIN, 30)
}
/**
 * Big margins
 */
trait ButtonDefaults extends Button with Defaults {
 margin = new java.awt.Insets(5,5,5,5)
}

trait StretchFull extends Component {
 maximumSize = new Dimension(Math.MAX_INT, Math.MAX_INT)
}


/**
 * Adds a round red rectangle when not valid, and fires events
 */
trait ValidationDecorated extends Component with Publisher {
 case class Valid(source: Component) extends event.ComponentEvent
 case class Invalid(source: Component) extends event.ComponentEvent
 private var _valid = true
 def valid_=(v:Boolean) {
   val old = _valid
   _valid = v
//    if(old != v) {
     if(v) publish(Valid(this)) else publish(Invalid(this))
//    }
   repaint
 }
 def valid = _valid

 override def paint(g: java.awt.Graphics) = g match {
   case g: java.awt.Graphics2D =>
     super.paint(g)
     if(!valid) {
       g.setPaint(new java.awt.Color(1f,0f,0f,.75f))
       g.setStroke(new java.awt.BasicStroke(3f))
       g.drawRoundRect(0,0,size.width-1,size.height-1, 20,20)
     }
 }
}

/**
 * Wrapper for SwingX JXTitledPanel
 */
class TitledPanel extends Component {
 import org.jdesktop.swingx.JXTitledPanel
 override lazy val peer: JXTitledPanel = new JXTitledPanel

 def title = peer.getTitle
 def title_=(t: String) = peer.setTitle(t)

 def titleFont = peer.getTitleFont
 def titleFont_=(f: Font) = peer.setTitleFont(f)

 def content =
peer.getContentContainer.asInstanceOf[javax.swing.JComponent].getClientProperty("scala.swingWrapper").asInstanceOf[Component]
 def content_=(c: Component) = peer.setContentContainer(c.peer)
}






Effects: (take advantage of traits)
package chavrusa.frontend

import java.awt.Graphics2D
import java.awt.image.BufferedImage

import scala.swing._


trait EffectPainting extends Component {
 import java.awt.image.BufferedImage
 var paintingEffect = false
 var paintingBackground = true

 override def paint(g: java.awt.Graphics) = g match {
   case g: Graphics2D =>
     if(!paintingEffect) {
       super.paint(g)
     } else {
       val bi = new BufferedImage(size.width, size.height,
BufferedImage.TYPE_4BYTE_ABGR)
       val gBi = bi.createGraphics
       if(paintingBackground) {
         gBi.setPaint(background)
         gBi.fillRect(0,0,size.width,size.height)
       } /*else {
         gBi.clearRect(0,0,size.width,size.height)
       }*/
       super.paint(gBi)
       paintEffect(bi, g)
     }
 }
//  override def paintComponent(g: Graphics) {}

 def paintEffect(bi: BufferedImage, g: Graphics2D)
}
trait Translucent extends EffectPainting {
 private var _opacity = 1f
 def opacity = _opacity
 def opacity_=(v: Float) = {
   _opacity = v
   repaint
 }

 def paintEffect(bi: java.awt.image.BufferedImage, g: Graphics2D) {
   val oldComposite = g.getComposite
   g.setComposite(java.awt.AlphaComposite.getInstance(
     java.awt.AlphaComposite.SRC_OVER,
     Math.min(1,Math.max(0,opacity))
   ))
   g.drawImage(bi,0,0,null)
   g.setComposite(oldComposite)
 }
}

trait TransitionEffect extends EffectPainting {
 var initialImage: Option[BufferedImage] = None
 var transitionValue = 0f
}
trait FadeTransition extends TransitionEffect {
 def paintEffect(bi: BufferedImage, g0: Graphics2D) {
   val g = g0.create.asInstanceOf[Graphics2D]
   initialImage match {
     case Some(img) =>
       g.drawImage(img,0,0,null)
       g.setComposite(java.awt.AlphaComposite.getInstance(
         java.awt.AlphaComposite.SRC_OVER,
         Math.min(1,Math.max(0,transitionValue))
       ))
     case None =>
   }
   g.drawImage(bi,0,0,null)
 }
}


Re: Re: Some scala-swing ideas

by Kevin Wright-4 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Sorry Naftoli...

I'm trying to track swing stuff as closely as I can, and this one is
on my todo list.  But new job, and a baby, and some fun
maven+udson+scala stuff I'm playing with right now, I seem to be a
little over-committed :)

You're not being ignored though!


On Mon, Oct 12, 2009 at 8:31 PM, Naftoli Gugenheim <naftoligug@...> wrote:

> No comments?
>
> On Mon, Oct 5, 2009 at 3:28 PM, Naftoli Gugenheim <naftoligug@...>
> wrote:
>>
>> As part of an application I'm writing, I wrote some code, part of
>> which may be useful for scala-swing. It's not very polished, but I'm
>> copy-pasting some code that I wrote so more eyes will see it. Please
>> comment!
>>
>> Components:
>>
>> package chavrusa.frontend
>>
>> import scala.swing._
>>
>> import scala.collection.mutable.Map
>>
>> import java.awt.{Font, Dimension}
>>
>> /**
>>  * A component, with control of get/setComponentZOrder.
>>  * Uses OverlayLayout.
>>  */
>> trait ZOrdered extends Component with SequentialContainer.Wrapper {
>>  override lazy val peer: javax.swing.JPanel = {
>>    val p = new javax.swing.JPanel with SuperMixin
>>    p.setLayout(new javax.swing.OverlayLayout(p))
>>    p
>>  }
>>  lazy val zOrder: Map[Component, Int] = new Map[Component, Int] {
>>    def -=(c: Component) { _contents -= c }
>>    def update(c: Component, z: Int) = {
>>      peer.add(c.peer)
>>      peer.setComponentZOrder(c.peer, z)
>>    }
>>    def get(c: Component) = Some(peer.getComponentZOrder(c.peer))
>>    def size = contents.size
>>    def elements: Iterator[(Component, Int)] =
>>      contents.elements map {c => (c, apply(c))}
>>  }
>>  def toFront(c: Component) = zOrder(c) = 0
>>  def toBack(c: Component) = zOrder(c) = -1
>> }
>>
>> /**
>>  * A JLayeredPane wrapper using OverlayLayout
>>  */
>> class OverlayLayeredPane extends SequentialContainer.Wrapper {
>>  override lazy val peer: javax.swing.JLayeredPane = {
>>    val lp = new javax.swing.JLayeredPane with SuperMixin
>>    lp.setLayout(new javax.swing.OverlayLayout(lp))
>>    lp
>>  }
>>
>>  lazy val layer: Map[Component, (Int,Int)] = new Map[Component, (Int,
>> Int)] {
>>    def -=(c: Component) { _contents -= c }
>>    def update(c: Component, i: (Int,Int)) = {
>>      i match {
>>        case (layer, pos) =>
>>          peer.setLayer(c.peer, layer)
>>          peer.add(c.peer)
>>          peer.setPosition(c.peer, pos)
>>      }
>>    }
>>    def get(c: Component) = Some((peer.getLayer(c.peer),
>> peer.getPosition(c.peer)))
>>    def size = contents.size
>>    def elements: Iterator[(Component, (Int,Int))] =
>>      contents.elements map {c => (c, apply(c))}
>>  }
>>
>>  def toFront(c: Component) = {
>>    println(peer.getComponents contains c.peer)
>>    peer.moveToFront(c.peer)
>>  }
>>
>> }
>>
>>
>> /**
>>  * Default settings for this particular kiosk.
>>  * Big font
>>  */
>> trait Defaults extends Component {
>>  xLayoutAlignment = 0.5
>>  font = new Font("SansSerif", java.awt.Font.PLAIN, 30)
>> }
>> /**
>>  * Big margins
>>  */
>> trait ButtonDefaults extends Button with Defaults {
>>  margin = new java.awt.Insets(5,5,5,5)
>> }
>>
>> trait StretchFull extends Component {
>>  maximumSize = new Dimension(Math.MAX_INT, Math.MAX_INT)
>> }
>>
>>
>> /**
>>  * Adds a round red rectangle when not valid, and fires events
>>  */
>> trait ValidationDecorated extends Component with Publisher {
>>  case class Valid(source: Component) extends event.ComponentEvent
>>  case class Invalid(source: Component) extends event.ComponentEvent
>>  private var _valid = true
>>  def valid_=(v:Boolean) {
>>    val old = _valid
>>    _valid = v
>> //    if(old != v) {
>>      if(v) publish(Valid(this)) else publish(Invalid(this))
>> //    }
>>    repaint
>>  }
>>  def valid = _valid
>>
>>  override def paint(g: java.awt.Graphics) = g match {
>>    case g: java.awt.Graphics2D =>
>>      super.paint(g)
>>      if(!valid) {
>>        g.setPaint(new java.awt.Color(1f,0f,0f,.75f))
>>        g.setStroke(new java.awt.BasicStroke(3f))
>>        g.drawRoundRect(0,0,size.width-1,size.height-1, 20,20)
>>      }
>>  }
>> }
>>
>> /**
>>  * Wrapper for SwingX JXTitledPanel
>>  */
>> class TitledPanel extends Component {
>>  import org.jdesktop.swingx.JXTitledPanel
>>  override lazy val peer: JXTitledPanel = new JXTitledPanel
>>
>>  def title = peer.getTitle
>>  def title_=(t: String) = peer.setTitle(t)
>>
>>  def titleFont = peer.getTitleFont
>>  def titleFont_=(f: Font) = peer.setTitleFont(f)
>>
>>  def content =
>>
>> peer.getContentContainer.asInstanceOf[javax.swing.JComponent].getClientProperty("scala.swingWrapper").asInstanceOf[Component]
>>  def content_=(c: Component) = peer.setContentContainer(c.peer)
>> }
>>
>>
>>
>>
>>
>>
>> Effects: (take advantage of traits)
>> package chavrusa.frontend
>>
>> import java.awt.Graphics2D
>> import java.awt.image.BufferedImage
>>
>> import scala.swing._
>>
>>
>> trait EffectPainting extends Component {
>>  import java.awt.image.BufferedImage
>>  var paintingEffect = false
>>  var paintingBackground = true
>>
>>  override def paint(g: java.awt.Graphics) = g match {
>>    case g: Graphics2D =>
>>      if(!paintingEffect) {
>>        super.paint(g)
>>      } else {
>>        val bi = new BufferedImage(size.width, size.height,
>> BufferedImage.TYPE_4BYTE_ABGR)
>>        val gBi = bi.createGraphics
>>        if(paintingBackground) {
>>          gBi.setPaint(background)
>>          gBi.fillRect(0,0,size.width,size.height)
>>        } /*else {
>>          gBi.clearRect(0,0,size.width,size.height)
>>        }*/
>>        super.paint(gBi)
>>        paintEffect(bi, g)
>>      }
>>  }
>> //  override def paintComponent(g: Graphics) {}
>>
>>  def paintEffect(bi: BufferedImage, g: Graphics2D)
>> }
>> trait Translucent extends EffectPainting {
>>  private var _opacity = 1f
>>  def opacity = _opacity
>>  def opacity_=(v: Float) = {
>>    _opacity = v
>>    repaint
>>  }
>>
>>  def paintEffect(bi: java.awt.image.BufferedImage, g: Graphics2D) {
>>    val oldComposite = g.getComposite
>>    g.setComposite(java.awt.AlphaComposite.getInstance(
>>      java.awt.AlphaComposite.SRC_OVER,
>>      Math.min(1,Math.max(0,opacity))
>>    ))
>>    g.drawImage(bi,0,0,null)
>>    g.setComposite(oldComposite)
>>  }
>> }
>>
>> trait TransitionEffect extends EffectPainting {
>>  var initialImage: Option[BufferedImage] = None
>>  var transitionValue = 0f
>> }
>> trait FadeTransition extends TransitionEffect {
>>  def paintEffect(bi: BufferedImage, g0: Graphics2D) {
>>    val g = g0.create.asInstanceOf[Graphics2D]
>>    initialImage match {
>>      case Some(img) =>
>>        g.drawImage(img,0,0,null)
>>        g.setComposite(java.awt.AlphaComposite.getInstance(
>>          java.awt.AlphaComposite.SRC_OVER,
>>          Math.min(1,Math.max(0,transitionValue))
>>        ))
>>      case None =>
>>    }
>>    g.drawImage(bi,0,0,null)
>>  }
>> }
>
>

Scala-Interpreter Oddity

by phkoester :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I just did a small experiment on the command line:

*

$ scala
Welcome to Scala version 2.7.6.final (Java HotSpot(TM) Client VM, Java
1.6.0_16).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A
defined class A

scala> println(classOf[A])
class line1$object$$iw$$iw$A

scala> println(classOf[A].getName)
line1$object$$iw$$iw$A

scala> println(classOf[A].getCanonicalName)
java.lang.ClassNotFoundException: $iw
         at
scala.tools.nsc.interpreter.AbstractFileClassLoader.findClass(AbstractFileClassLoader.scala:27)
         at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
         at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
         at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
         at java.lang.Class.getDeclaringClass(Native Method)
         at java.lang....
scala>

*

I define a class `A' and use `toString' and `getName' it, both of which
work.

But then, `classOf[A].getCanonicalName)' yields in a `CNFE'. What gives?

---Ph.

Re: Scala-Interpreter Oddity -- General Class-Name Curio

by phkoester :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

To elaborate on my question, the following code surfaces the same problem:

        val name = "scala.xml.pull.ProducerConsumerIterator$$anonfun$fillBuffer$1"
        val c = Class.forName(name) // Works
        println(c.getName) // Works, will print
`scala.xml.pull.ProducerConsumerIterator$$anonfun$fillBuffer$1'
        println(c.getCanonicalName)

Here, `getCanonicalName' will yield an `IncompatibleClassChangeError':

        Exception in thread "main" java.lang.IncompatibleClassChangeError:
scala.xml.pull.ProducerConsumerIterator and
scala.xml.pull.ProducerConsumerIterator$$anonfun$fillBuffer$1 disagree
on InnerClasses attribute
                at java.lang.Class.getDeclaringClass(Native Method)
                at java.lang.Class.getEnclosingClass(Class.java:1085)
                at java.lang.Class.getCanonicalName(Class.java:1169)
                at com.phrood.sack.Sack.run(Sack.scala:48)
                at com.phrood.sack.Sack$.main(Sack.scala:25)
                at com.phrood.sack.Sack.main(Sack.scala)

The same thing happens if I use `Class.getSimpleName'.

This effectively means that it is no longer safe to use
`Class.getCanonicalName' and `Class.getSimpleName' on classes that have
already been successfully loaded. Maybe we need a new and smarter JDK
implementation of `Class.getEnclosingClass' to make it Scala-ready?

I guess most code around will deal with `ClassNotFoundException' after
calling `Class.forName' since it is a checked exception, but I don't
think anyone would expect `getCanonicalName' and `getSimpleName' to fail
for an existing `Class' instance. This problem not just might but *will*
break existing programs, especially class-analyzing tools.

It looks like this little innocent dollar sign in class names poses more
problems that one would initially expect. It also broke the AspectJ
weaver, which will have a patch as of version 1.6.7. I cannot cite the
JLS by heart here, but I seem to remember that dollar signs in class
names (more generally, in identifiers) are legal, albeit unrecommended
Java, so the JDK should be able to deal with it.

Cheers
---Ph.

Re: Scala-Interpreter Oddity -- General Class-Name Curio

by Johannes Rudolph-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I think this could be a bug in the Scala backend.

First, here is a simpler example:

trait Test {
 def testFunc(i:Int) = ((i:Int) => i + 5)(i)
}

And here the console session:

Welcome to Scala version 2.7.4.final (OpenJDK 64-Bit Server VM, Java 1.6.0_0).
Type in expressions to have them evaluated.
Type :help for more information.

scala> Class.forName("Test$$anonfun$testFunc$1")
res0: java.lang.Class[_] = class Test$$anonfun$testFunc$1

scala> res0.getSimpleName
java.lang.IncompatibleClassChangeError: Test and
Test$$anonfun$testFunc$1 disagree on InnerClasses attribute
        at java.lang.Class.getDeclaringClass(Native Method)
        at java.lang.Class.getEnclosingClass(Class.java:1102)
        at java.lang.Class.getSimpleBinaryName(Class.java:1237)
        at java.lang.Class.getSimpleName(Class.java:1129)
        at .<init>(<console>:6)
        at .<clinit>(<console>)
        at RequestR...

And here the relevant part of the javap output:

% javap -v Test\$\$anonfun\$testFunc\$1
Compiled from "test.scala"
public final class Test$$anonfun$testFunc$1 extends java.lang.Object
implements scala.Function1,scala.ScalaObject,java.io.Serializable
  SourceFile: "test.scala"
  Scala: length = 0x

  InnerClass:
   public final #72= #32 of #71; //$anonfun$testFunc$1=class
Test$$anonfun$testFunc$1 of class Test

If I understand the JVM spec correctly, there are several problems at once:

 * Scala's compilation strategy for traits is to generate an interface
(Test) and another class containing the implementations (Test$class).
The anonymous function being an implementation detail is therefore an
inner class of Test$class and not of Test. So the InnerClass entry
refers to the wrong class. If one looks at the Test and Test$class one
can see that it actually _is_ declared in Test$class with exactly the
same InnerClass entry as here (outer class = Test), which is kind of
paradox.
 * That aside, InnerClass entries for anonymous classes are compiled
differently in Java: Name and OuterClass are null and only the
innerClass member of the InnerClass data structure should be set. The
real back-reference is made through an EnclosingMethod entry. [1]

For reference I appended a small Java example useful to examine the
differences when compiling inner/nested classes.

That said, I should add as an disclaimer, that since I wasn't involved
in implementing Scala's backend at all, there may be reasons for
generating class-files like that.

Johannes

BTW: I'm not sure if your last mail is a result of the same bug or
another problem with the interpreter's classpath.

[1] From JVM Spec §4.7.5: "If C is anonymous, the value of the
inner_name_index item must be zero." and "If C is not a member, the
value of the outer_class_info_index item must be zero."

On Tue, Oct 13, 2009 at 9:08 AM, Philip Köster <philip.koester@...> wrote:

> To elaborate on my question, the following code surfaces the same problem:
>
>        val name =
> "scala.xml.pull.ProducerConsumerIterator$$anonfun$fillBuffer$1"
>        val c = Class.forName(name) // Works
>        println(c.getName) // Works, will print
> `scala.xml.pull.ProducerConsumerIterator$$anonfun$fillBuffer$1'
>        println(c.getCanonicalName)
>
> Here, `getCanonicalName' will yield an `IncompatibleClassChangeError':
>
>        Exception in thread "main" java.lang.IncompatibleClassChangeError:
> scala.xml.pull.ProducerConsumerIterator and
> scala.xml.pull.ProducerConsumerIterator$$anonfun$fillBuffer$1 disagree on
> InnerClasses attribute
>                at java.lang.Class.getDeclaringClass(Native Method)
>                at java.lang.Class.getEnclosingClass(Class.java:1085)
>                at java.lang.Class.getCanonicalName(Class.java:1169)
>                at com.phrood.sack.Sack.run(Sack.scala:48)
>                at com.phrood.sack.Sack$.main(Sack.scala:25)
>                at com.phrood.sack.Sack.main(Sack.scala)
>
> The same thing happens if I use `Class.getSimpleName'.
>
> This effectively means that it is no longer safe to use
> `Class.getCanonicalName' and `Class.getSimpleName' on classes that have
> already been successfully loaded. Maybe we need a new and smarter JDK
> implementation of `Class.getEnclosingClass' to make it Scala-ready?
>
> I guess most code around will deal with `ClassNotFoundException' after
> calling `Class.forName' since it is a checked exception, but I don't think
> anyone would expect `getCanonicalName' and `getSimpleName' to fail for an
> existing `Class' instance. This problem not just might but *will* break
> existing programs, especially class-analyzing tools.
>
> It looks like this little innocent dollar sign in class names poses more
> problems that one would initially expect. It also broke the AspectJ weaver,
> which will have a patch as of version 1.6.7. I cannot cite the JLS by heart
> here, but I seem to remember that dollar signs in class names (more
> generally, in identifiers) are legal, albeit unrecommended Java, so the JDK
> should be able to deal with it.
>
> Cheers
> ---Ph.
>


--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

[test.java]

class JavaTest{
  static class InnerStaticClass{}
  class InnerClass{}
  public static void staticTest(){
    class InnerMethodClass{};
    new Object(){};
  }
}


Parent Message unknown Re: Scala-Interpreter Oddity -- General Class-Name Curio

by phkoester :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> BTW: I'm not sure if your last mail is a result of the same bug or
> another problem with the interpreter's classpath.

Me neither. In the Scala console, a `ClassNotFoundException' was raised, and in the example I ran in Eclipse, as well as in yours, it was an `IncompatibleClassChangeError'. I don't know if these are two separate problems or if they eventually boil down to the same implementation issue in `java.lang.Class' or elsewhere.

Anyway, something has got to give. :) Either this is an essential bug in the Scala-generated bytecode, or the JRE's `Class' needs a fix on the Java or native side.

Who feels responsible or should be contacted?

---Ph.

Re: Scala-Interpreter Oddity -- General Class-Name Curio

by Johannes Rudolph-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Here are two related tickets:

http://lampsvn.epfl.ch/trac/scala/ticket/1167
is a consequent bug of this issue and was closed because a workaround
was available.

http://lampsvn.epfl.ch/trac/scala/ticket/1572
there are some interesting comments how inner classes should be generated.

It would be interesting to know if the current behaviour is just an
oversight and sloppy implementation of the JVM specs or if there is
some deeper rational behind.
In the end it is questionable if this issue has any practical
relevance at all (apart having to deal with crashes in unexpected
situations). Is there any real use for this metadata?

Johannes

On Tue, Oct 13, 2009 at 12:11 PM,  <philip.koester@...> wrote:

>> BTW: I'm not sure if your last mail is a result of the same bug or
>> another problem with the interpreter's classpath.
>
> Me neither. In the Scala console, a `ClassNotFoundException' was raised, and in the example I ran in Eclipse, as well as in yours, it was an `IncompatibleClassChangeError'. I don't know if these are two separate problems or if they eventually boil down to the same implementation issue in `java.lang.Class' or elsewhere.
>
> Anyway, something has got to give. :) Either this is an essential bug in the Scala-generated bytecode, or the JRE's `Class' needs a fix on the Java or native side.
>
> Who feels responsible or should be contacted?
>
> ---Ph.
>



--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Parent Message unknown Re: Scala-Interpreter Oddity -- General Class-Name Curio

by phkoester :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Johannes,

> Here are two related tickets:
>
> http://lampsvn.epfl.ch/trac/scala/ticket/1167

why was the ticket closed? I see no discussion about an existing work-around.

> http://lampsvn.epfl.ch/trac/scala/ticket/1572

In Eclipse, I'm using Scala Library 2.8.0.r19052, and in the console it's 2.8.0.r18462 (with `maven-scala-plugin'). Maybe the fix that is mentioned in ticket # 1572 is included in 19052, but I still get this `IncompatibleClassChangeError' in both environments.

> It would be interesting to know if the current behaviour is just an
> oversight and sloppy implementation of the JVM specs or if there is
> some deeper rational behind.
> In the end it is questionable if this issue has any practical
> relevance at all (apart having to deal with crashes in unexpected
> situations). Is there any real use for this metadata?

Calling `getCanonicalName' or `getSimpleName' on a `Class' instance is a fundamental thing to do that should just work and not throw any unchecked exceptions. Canonical class names and simple names, as well as inner-outer relationships, are very useful for class-analyzing and debugging purposes. In a sense, hoping this is understandable English, these are bread-and-butter tools for me.

I think something as simple as `scala> class A; classOf[A].getCanonicalName' just may not fail. This reveals there is something shaky at the very bottom of the class layout.

Re: Scala-Interpreter Oddity -- General Class-Name Curio

by Johannes Rudolph-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, Oct 14, 2009 at 10:36 AM,  <philip.koester@...> wrote:
>> http://lampsvn.epfl.ch/trac/scala/ticket/1167
>
> why was the ticket closed? I see no discussion about an existing work-around.

I thought it was closed because of the comment

"Changing the one line to [...] Makes the problem go away. "

but as it turned out, it was closed because of a fix that was
committed and which is exactly the fix I proposed in one of the
earlier mails. Unfortunately this fix was removed later on in another
commit and the problem seems to reappear.

>> http://lampsvn.epfl.ch/trac/scala/ticket/1572
>
> In Eclipse, I'm using Scala Library 2.8.0.r19052, and in the console it's 2.8.0.r18462 (with `maven-scala-plugin'). Maybe the fix that is mentioned in ticket # 1572 is included in 19052, but I still get this `IncompatibleClassChangeError' in both environments.

The solution to this particular issue is not a fix for your bug. I
added it just for some background.

> I think something as simple as `scala> class A; classOf[A].getCanonicalName' just may not fail. This reveals there is something shaky at the very bottom of the class layout.

Your are generally right, but what I wanted to say is that the very
long period this bug went undiscovered is evidence for its relative
unimportance.

--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Parent Message unknown Re: Scala-Interpreter Oddity -- General Class-Name Curio

by phkoester :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

> Your are generally right, but what I wanted to say is that the very
> long period this bug went undiscovered is evidence for its relative
> unimportance.

You have a point here. I guess this bug is likely to surface as more and more libraries written in Scala become available.

Addendum: `Class.getDeclaringClass' and `Class.getEnclosingClass' don't work reliably either. I no longer think this is a bagatelle.


Re: Scala-Interpreter Oddity -- General Class-Name Curio

by Matt Hellige :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Wed, Oct 14, 2009 at 4:13 AM,  <philip.koester@...> wrote:
>> Your are generally right, but what I wanted to say is that the very
>> long period this bug went undiscovered is evidence for its relative
>> unimportance.
>
> You have a point here. I guess this bug is likely to surface as more and more libraries written in Scala become available.
>
> Addendum: `Class.getDeclaringClass' and `Class.getEnclosingClass' don't work reliably either. I no longer think this is a bagatelle.


I hate to revisit an old thread, but this issue has come up for me
twice in the past month. The first time, I could find a workaround,
but this time I cannot. I'm using a third-party library which does
some simple reflection, and I'm getting:

  java.lang.IncompatibleClassChangeError: scala.collection.Map and
scala.collection.Map$$anon$5 disagree on InnerClasses attribute
        at java.lang.Class.getDeclaringClass(Native Method)
        at java.lang.Class.getEnclosingClass(Class.java:1085)
        [...]

Is there any fix for this? Any workaround? Neither the offending code
nor the client code are under my control. Seems like bug 1167 is still
sitting there... Anybody?

Thanks...
Matt