From d4e3c78e738e37a0ba5fa5eb8b5196a8556d2c53 Mon Sep 17 00:00:00 2001 From: buraq Date: Fri, 25 Jun 2004 17:29:50 +0000 Subject: namespaces --- Makefile.config | 2 +- config/list/library.lst | 5 +- config/list/scalac.lst | 1 + sources/scala/Predef.scala | 2 + sources/scala/tools/dtd2scala/DeclToScala.scala | 23 +- sources/scala/tools/dtd2scala/Main.scala | 14 +- .../dtd2scala/template/ObjectTemplate.scala.xml | 83 ++-- .../tools/scalac/ast/parser/MarkupParser.scala | 398 ++---------------- .../scala/tools/scalac/ast/parser/Scanner.scala | 2 +- .../scalac/ast/parser/SymbolicXMLBuilder.scala | 453 +++++++++++++++++++++ sources/scala/xml/Attribute.scala | 29 ++ sources/scala/xml/AttributeSeq.scala | 69 ++++ sources/scala/xml/BindingFactoryAdapter.scala | 10 +- sources/scala/xml/CharData.scala | 11 +- sources/scala/xml/Elem.scala | 26 +- sources/scala/xml/FactoryAdapter.scala | 11 +- sources/scala/xml/Namespace.scala | 14 - sources/scala/xml/NamespaceRegistry.scala | 43 -- sources/scala/xml/Node.scala | 26 +- sources/scala/xml/PrettyPrinter.scala | 104 +++-- sources/scala/xml/SpecialNode.scala | 2 +- sources/scala/xml/Utility.scala | 206 ++++++++-- sources/scala/xml/dtd/Decl.scala | 36 +- sources/scala/xml/dtd/Validation.scala | 92 +++++ .../xml/nobinding/NoBindingFactoryAdapter.scala | 6 +- .../scalac/transformer/matching/BerrySethi.java | 7 +- sources/scalac/transformer/matching/Label.java | 2 +- test/bin/scala-test | 3 +- test/files/jvm/xmlLiterals.scala | 6 +- test/files/jvm/xmlstuff.check | 9 + test/files/jvm/xmlstuff.scala | 52 ++- test/files/xml/lnk.check | 4 +- test/files/xml/lnk.namespace | 1 + test/files/xml/lnk.scala | 9 +- test/files/xml/xhtml.check | 4 +- test/files/xml/xhtml.namespace | 1 + test/files/xml/xhtml.scala | 15 +- 37 files changed, 1139 insertions(+), 642 deletions(-) create mode 100644 sources/scala/tools/scalac/ast/parser/SymbolicXMLBuilder.scala create mode 100644 sources/scala/xml/Attribute.scala create mode 100644 sources/scala/xml/AttributeSeq.scala delete mode 100644 sources/scala/xml/Namespace.scala delete mode 100644 sources/scala/xml/NamespaceRegistry.scala create mode 100644 sources/scala/xml/dtd/Validation.scala create mode 100644 test/files/xml/lnk.namespace create mode 100644 test/files/xml/xhtml.namespace diff --git a/Makefile.config b/Makefile.config index 15b4443881..52872f2971 100644 --- a/Makefile.config +++ b/Makefile.config @@ -105,7 +105,7 @@ PICO_FLAGS ?= -make -source 1.4 ############################################################################## # Bootstrap compiler -BOOTSTRAP_SCALAC ?= scalac +BOOTSTRAP_SCALAC ?= /home/linuxsoft/apps/scala/bin/scalac ############################################################################## # Convert tool (ImageMagick) diff --git a/config/list/library.lst b/config/list/library.lst index f0eefeea51..b4000ffc06 100644 --- a/config/list/library.lst +++ b/config/list/library.lst @@ -155,6 +155,8 @@ runtime/matching/Rule.scala testing/UnitTest.scala +xml/Attribute.scala +xml/AttributeSeq.scala xml/BindingFactoryAdapter.scala xml/CharData.scala xml/Comment.scala @@ -162,8 +164,6 @@ xml/Elem.scala xml/ExternalID.scala xml/EntityRef.scala xml/FactoryAdapter.scala -xml/Namespace.scala -xml/NamespaceRegistry.scala xml/Node.scala xml/NodeBuffer.scala xml/NodeSeq.scala @@ -180,6 +180,7 @@ xml/dtd/Parser.scala xml/dtd/RegExp.scala xml/dtd/Scanner.scala xml/dtd/Tokens.scala +xml/dtd/Validation.scala xml/nobinding/NoBindingFactoryAdapter.scala xml/nobinding/XML.scala diff --git a/config/list/scalac.lst b/config/list/scalac.lst index dd2550b0aa..acaa2db448 100644 --- a/config/list/scalac.lst +++ b/config/list/scalac.lst @@ -14,6 +14,7 @@ ast/parser/Parser.scala ast/parser/ParserPhase.scala ast/parser/PatternNormalizer.scala ast/parser/Scanner.scala +ast/parser/SymbolicXMLBuilder.scala ast/parser/TokenData.scala ast/parser/Tokens.scala diff --git a/sources/scala/Predef.scala b/sources/scala/Predef.scala index 7dd8bc3a60..87d92741e3 100644 --- a/sources/scala/Predef.scala +++ b/sources/scala/Predef.scala @@ -42,6 +42,8 @@ object Predef { def fst[a](x: a, y: Any): a = x; def scd[a](x: Any, y: a): a = y; + val namespace$default = ""; + type Function[-a,+b] = Function1[a,b]; // arrays ----------------------------------------------------------- diff --git a/sources/scala/tools/dtd2scala/DeclToScala.scala b/sources/scala/tools/dtd2scala/DeclToScala.scala index 0fbb396dc5..62a724187f 100644 --- a/sources/scala/tools/dtd2scala/DeclToScala.scala +++ b/sources/scala/tools/dtd2scala/DeclToScala.scala @@ -19,12 +19,9 @@ import scala.xml.nobinding.XML ; /** transforms a set of DTD declaraion to a scala source file. * 2do: parameterize with destination package. */ -class DeclToScala(fOut:PrintWriter, - moduleName:String, - elemMap:Map[ String, MyElemDecl ] ) { +class DeclToScala(fOut: PrintWriter, objectName: String, namespace: String, elemMap:Map[String, MyElemDecl] ) { - abstract class ObjectTemplate { - val objectName : String = "myXML"; /* DEFAULT MODULE NAME */ + class ObjectTemplate { val package_ : String = ""; val compress : boolean = true; final val tmplFile = "scala/tools/dtd2scala/template/ObjectTemplate.scala.xml"; @@ -60,7 +57,7 @@ class DeclToScala(fOut:PrintWriter, } /** 1.populate with actual values: throw error if FIXED is wrong ** 2.throw error if a REQUIRED one is missing*/ - def validateAttributes( curAttribs:Map[String,AttrDecl] ):String = { + def validateAttributes(curAttribs: Map[String,AttrDecl]):String = { def appendKey( key:String, sb:StringBuffer ) = { val it = Iterator.fromString( key ); sb.append('\''); @@ -169,8 +166,17 @@ class DeclToScala(fOut:PrintWriter, fOut.print( text ); case n:Elem => n.label match { + case "attributeDecls" => + var sb = new StringBuffer("Nil"); + for( val aDecl <- curAttribs.values ) { + sb.insert(0, "::"); + sb.insert(0, aDecl.toString()); + } + fOut.print(sb.toString()); + case "template" => { lookup.update("objectName", objectName); + lookup.update("namespace", namespace); lookup.update("compressDefault", compress.toString()); n.child.elements.foreach { n => writeNode(n) } } @@ -212,6 +218,7 @@ class DeclToScala(fOut:PrintWriter, case "attributeBinding" => { for( val aDecl <- curAttribs.keys ) { lookup += "attributeName" -> aDecl; + lookup += "attributeDecl" -> curAttribs(aDecl).toString(); n.child.elements.foreach{ n => writeNode( n ) } } lookup -= "attributeName"; @@ -240,9 +247,7 @@ class DeclToScala(fOut:PrintWriter, /** runs translation. */ def run:Unit = { - new ObjectTemplate { - override val objectName = moduleName - }.write; + new ObjectTemplate().write; fOut.flush(); fOut.close(); } diff --git a/sources/scala/tools/dtd2scala/Main.scala b/sources/scala/tools/dtd2scala/Main.scala index 5daa917d8a..b4f292ac46 100644 --- a/sources/scala/tools/dtd2scala/Main.scala +++ b/sources/scala/tools/dtd2scala/Main.scala @@ -23,7 +23,7 @@ object Main { def printUsage:Unit = { - Console.println("usage: dtd2scala [ -d ] "); + Console.println("usage: dtd2scala [ -d ] []"); Console.println(" binds a DTD to class definitions"); Console.println(" will create a file [/].scala"); Console.println(" is the output directory [path of is default]"); @@ -37,23 +37,27 @@ object Main { List.fromArray(argv, 0, argv.length) match { case Seq( "-d", outdir, sysID, objName ) => - continue( new File( outdir ), sysID, objName ); + continue( new File( outdir ), sysID, objName, "" ); + case Seq( "-d", outdir, sysID, objName, namespace ) => + continue( new File( outdir ), sysID, objName, namespace ); //case Seq( "-sql", sysID ) => dosql( sysID ); case Seq( sysID, objName ) => - continue( new File( sysID ).getParentFile(), sysID, objName ); + continue( new File( sysID ).getParentFile(), sysID, objName, "" ); + case Seq( sysID, objName, namespace ) => + continue( new File( sysID ).getParentFile(), sysID, objName, namespace ); case _ => { printUsage; System.exit(-1); } } } - private def continue( outdir:File, sysID:String, objName:String ) = { + private def continue( outdir:File, sysID:String, objName:String, ns:String ) = { val myH:MainHandler = new MainHandler(); parse( sysID, myH ); // myH.print(); // DEBUG val p = new PrintWriter(new FileWriter(new File(outdir, objName+".scala" ))); - new DeclToScala( p, objName, myH.elemMap ).run; + new DeclToScala( p, objName, ns, myH.elemMap ).run; } /* diff --git a/sources/scala/tools/dtd2scala/template/ObjectTemplate.scala.xml b/sources/scala/tools/dtd2scala/template/ObjectTemplate.scala.xml index 68b0db8546..07449413d4 100644 --- a/sources/scala/tools/dtd2scala/template/ObjectTemplate.scala.xml +++ b/sources/scala/tools/dtd2scala/template/ObjectTemplate.scala.xml @@ -29,19 +29,6 @@ import scala.collection.mutable; type cT = scala.Seq[scala.xml.Node]; - type aT = Map[String,String]; - - def error_FixedAttribute( k:String, value:String ) = - error("value of attribute " + k + " FIXED to \""+value+"\""); - - def error_UndefinedAttribute( b:String ) = - error("undefined attribute " + b ); - - def error_MissingAttribute( map:aT ) = - error("missing value for REQUIRED attribute " - +(map.keys.filter { x => map(x) == "<R" }) - .toList.mkString("",",","")); - def error_InvalidChildren( elName:String, ch:cT, cM:String ) = error("trying to construct invalid "+elName+",\n children labels were: " @@ -49,8 +36,8 @@ +"\n content model was "+cM); abstract class Node$ extends scala.xml.Node { - /** the namespace code of this node */ - final val namespaceCode: Int = 0; + /** the namespace of this node */ + final val namespace: String = ; } def tagIterator(ch:Seq[scala.xml.Node]):Iterator[Int] = new Iterator[Int] { @@ -79,53 +66,34 @@ - def constr_&ccElementName;( attrs:aT, ch:cT ) = &ccElementName;(attrs,ch:_*); + def constr_&ccElementName;( attrs:scala.xml.AttributeSeq, ch:cT ) = &ccElementName;(attrs,ch:_*); - case class &ccElementName;( attrs:Map[String,String], ch:scala.xml.Node* ) extends Node$ { + private val &ccElementName;AttribValidator = { + import scala.xml.dtd._; + new AttributeValidator( + , ); + } + case class &ccElementName;( attrs:scala.xml.AttributeSeq, ch:scala.xml.Node* ) extends Node$ { final override def typeTag$ = ; - final def shallowValidate&ccElementName;Children( ch:Seq[scala.xml.Node] ) = - ; - - final def validateAttributes( attrs:Map[String,String] ):aT = { - var req = 0; - var map = scala.xml.Node.NoAttributes; - - - for( val b <- attrs.elements ) { - val a:Seq[Char] = b._1; - a match { - - case _ => error_UndefinedAttribute( b._1 ) - } - } - if( req > 0 ) error_MissingAttribute( map ); - map - } - - if( !shallowValidate&ccElementName;Children( ch ) ) - error_InvalidChildren( &qElementName;, ch, ); - final def label = &qElementName;; - final def child = ch; - - protected var hmap:aT = _; - try { - hmap = validateAttributes( attrs ); - } catch { - case e:java.lang.Error => - error("invalid attributes for &elementName;\n"+e.getMessage()); - } - final def attribute:aT = hmap; + final def child = ch; + final def attributes = theAttrs; + + val theAttrs = &ccElementName;AttribValidator.validate( attrs ); - - final def &cAttributeName; : scala.Option[String] = hmap.get(&qAttributeName;); - - val attribHashCode: int = hmap.toList.hashCode() ; + + /* attributes + + */ - /** returns a new &ccElementName; with updated attributes */ + /** returns a new &ccElementName; with updated attributes final def %(attrs: Seq[Pair[String, String]]) = { var newmap = scala.xml.Node.NoAttributes; for( val p <- attribute.elements ) { @@ -136,8 +104,8 @@ } &ccElementName;( newmap, child:_* ) ; } - - /** returns a new &ccElementName; with updated attribute */ + */ + /** returns a new &ccElementName; with updated attribute final def %(attr: Pair[String, String]) = { var newmap = scala.xml.Node.NoAttributes; for( val p <- attribute.elements ) { @@ -146,6 +114,7 @@ newmap = newmap.update( attr._1, attr._2 ); &ccElementName;( newmap, child:_* ) ; } +*/ } @@ -155,7 +124,7 @@ def load( filename:String, _compress:boolean ):scala.xml.Node = { val fAdapter = new scala.xml.BindingFactoryAdapter { val f = { - val res = new mutable.HashMap[String, (aT,cT) => scala.xml.Node]() ; + val res = new mutable.HashMap[String, (scala.xml.AttributeSeq,cT) => scala.xml.Node]() ; res.update( &qElementName;, constr_&ccElementName;); res; diff --git a/sources/scala/tools/scalac/ast/parser/MarkupParser.scala b/sources/scala/tools/scalac/ast/parser/MarkupParser.scala index 19ef5493f3..b483b85aaa 100644 --- a/sources/scala/tools/scalac/ast/parser/MarkupParser.scala +++ b/sources/scala/tools/scalac/ast/parser/MarkupParser.scala @@ -10,7 +10,6 @@ import scalac.ast._; import scalac.atree.AConstant; import scalac._; import scalac.symtab.Modifiers; -import scalac.util.{ Name, Names, TypeNames }; import scala.tools.util.Position; import java.lang.{Integer, Long, Float, Double}; import scala.Iterator; @@ -18,6 +17,7 @@ import scala.tools.scalac.util.NewArray; import scala.collection.immutable.ListMap ; import scala.collection.mutable.Buffer; import scala.xml.{Text,TextBuffer}; +import scalac.util.Name; package scala.tools.scalac.ast.parser { @@ -26,358 +26,16 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { import Tokens.{EMPTY, LBRACE, RBRACE} ; import scala.tools.scalac.ast.{TreeList => myTreeList} - val _ArrayBuffer = Name.fromString("ArrayBuffer"); - val _NodeBuffer = Name.fromString("NodeBuffer"); - val _TreeMap = Name.fromString("TreeMap"); - val _Elem = Name.fromString("Elem"); - val _Seq = Name.fromString("Seq"); - val _String = Name.fromString("String"); - val _immutable = Name.fromString("immutable"); - val _mutable = Name.fromString("mutable"); - val _append = Name.fromString("append"); - val _plus = Name.fromString("$plus"); - val _collection = Name.fromString("collection"); - val _xml = Name.fromString("xml"); - val _Comment = Name.fromString("Comment"); - val _CharData = Name.fromString("CharData"); - val _Node = Name.fromString("Node"); - val _None = Name.fromString("None"); - val _Some = Name.fromString("Some"); - val _ProcInstr = Name.fromString("ProcInstr"); - val _Text = Name.fromString("Text"); - val _EntityRef = Name.fromString("EntityRef"); + /** the XML tree factory */ + val mk = new SymbolicXMLBuilder( unit.global.make, unit.global.treeGen, p, preserveWS ); - /** the tree factory - */ - val make: TreeFactory = unit.global.make; - - /** the tree generator - */ - val gen: TreeGen = unit.global.treeGen; + /** the XML tree builder */ + val gen = unit.global.treeGen ; var mode:boolean = false; final val PATTERN = true; final val EXPR = false; - // convenience methods - private def _scala( pos: int, name: Name ) = - make.Select( pos, make.Ident( pos, Names.scala ), name ); - - private def _scala_Seq( pos: int ) = - p.convertToTypeId( _scala( pos, _Seq )); - - - private def _scala_None( pos: int ) = - _scala( pos, _None ) ; - - private def _scala_Some( pos: int ) = - p.convertToConstr( _scala( pos, _Some )); - - - private def _string( pos: int ) = - p.convertToTypeId( make.Ident( pos, _String ) ); - - private def _scala_xml( pos: int, name: Name ) = - make.Select( pos, _scala( pos, _xml ), name ); - - private def _scala_xml_Node( pos: int ) = - _scala_xml( pos, _Node ); - - private def _scala_xml_NodeBuffer( pos: int ) = - p.convertToConstr( _scala_xml( pos, _NodeBuffer )); - - private def _scala_xml_EntityRef( pos: int ) = - p.convertToConstr( _scala_xml( pos, _EntityRef )); - - private def _scala_xml_Comment( pos: int ) = - p.convertToConstr( _scala_xml( pos, _Comment )); - - private def _scala_xml_CharData( pos: int ) = - p.convertToConstr( _scala_xml( pos, _CharData )); - - private def _scala_xml_ProcInstr( pos: int ) = - p.convertToConstr( _scala_xml( pos, _ProcInstr )); - - private def _scala_xml_Text( pos: int ) = - _scala_xml( pos, _Text ); - - - private def _scala_collection( pos: int, name: Name ) = - make.Select( pos, _scala( pos, _collection ), name ); - - private def _scala_collection_mutable( pos: int, name: Name ) = - make.Select(pos, _scala_collection(pos, _mutable ), name); - - private def _scala_collection_immutable( pos: int, name: Name ) = - make.Select(pos, _scala_collection(pos, _immutable ), name); - - private def _scala_collection_mutable_ArrayBuffer( pos: int ) = - make.Apply( pos, - make.AppliedType(pos, - p.convertToConstr( - _scala_collection_mutable(pos, _ArrayBuffer )), - Predef.Array[Tree]( - convertToTypeId( - _scala_xml_Node( pos ) )) - ), - Tree.EMPTY_ARRAY ); - - private def _scala_collection_immutable_TreeMap( pos: int ) = - make.Apply( pos, - make.AppliedType(pos, - p.convertToConstr( - _scala_collection_immutable(pos, _TreeMap )), - Predef.Array[Tree]( - _string( pos ), - _string( pos ) - ) - ), - Tree.EMPTY_ARRAY ); - - private def _emptyMap( pos:int ) = { - make.New( pos,_scala_collection_immutable_TreeMap( pos: int )); - } - - private def _scala_Tuple2( pos:int ) = - _scala( pos, Names.Tuple2 ); - - private def _scala_xml_Elem( pos:int ) = - _scala_xml( pos, _Elem ); - - /** convenience method */ - def convertToTypeId(t: Tree): Tree = t match { - case Tree$Ident(name) => - make.Ident(t.pos, name.toTypeName()) - case Tree$Select(qual, name) => - make.Select(t.pos, qual, name.toTypeName()) - case _ => - t - } - - // create scala xml tree - - /** - * @arg namespace: a Tree of type defs.STRING_TYPE - * @arg label: a Tree of type defs.STRING_TYPE - * @todo map: a map of attributes !!! - */ - def mkXML(pos:int, isPattern:boolean, namespace:Tree, label:Tree, args:Array[Tree]):Tree = { - if( isPattern ) { - val ts = new myTreeList(); - ts.append( namespace ); - ts.append( label ); - ts.append( new Tree$Ident( Names.PATTERN_WILDCARD ) ); - ts.append( convertToText( true, args ) ); - make.Apply(pos, - convertToTypeId( _scala_xml_Elem( pos ) ), - ts.toArray()) - } else { - val constrArgs = if( 0 == args.length ) { - Predef.Array[Tree]( namespace, label, _emptyMap( pos ) ) - } else { - Predef.Array[Tree]( namespace, label, _emptyMap( pos ), make.Typed( - pos, makeXMLseq(pos, args ), make.Ident(pos, TypeNames.WILDCARD_STAR))) - }; - make.Apply( pos, _scala_xml_Elem( pos ), constrArgs ) - } - } - - def makeEntityRef( pos:int, n:Name ) = { - val constr = make.Apply( pos, - _scala_xml_EntityRef( pos ), - Predef.Array[Tree]( gen.mkStringLit( pos, n.toString() ))); - - make.New( pos, constr ); - }; - // create scala.xml.Text here <: scala.xml.Node - def makeText( pos: int, isPattern:Boolean, txt:String ):Tree = { - makeText( pos, isPattern, gen.mkStringLit( pos, txt )); - } - - def appendTrimmed(pos: int, ts:myTreeList, txt:String) = { - var textNodes = - if( !preserveWS ) - new TextBuffer().append( txt ).toText; - else - List( Text( txt )); - - for( val t <- textNodes ) - ts.append( makeText( s.pos, mode, t.text )); - } - - // create scala.xml.Text here <: scala.xml.Node - def makeText( pos: int, isPattern:Boolean, txt:Tree ):Tree = { - if( isPattern ) - makeTextPat( pos, txt ); - else - makeText1( pos, txt ); - } - - // create scala.xml.Text here <: scala.xml.Node - def makeTextPat( pos: int, txt:Tree ):Tree = { - return make.Apply(pos, - p.convertToConstr( _scala_xml_Text( pos ) ), - Predef.Array[Tree]( txt )); - } - - def makeText1( pos: int, txt:Tree ):Tree = { - val constr = make.Apply(pos, - p.convertToConstr( _scala_xml_Text( pos )), - Predef.Array[Tree]( txt )); - make.New( pos, constr ); - } - - - // create - def makeComment( pos: int, comment:scala.xml.Comment ):Tree = - makeComment( pos, gen.mkStringLit( pos, comment.text )); - - // create - def makeCharData( pos: int, charData:scala.xml.CharData ):Tree = - makeCharData( pos, gen.mkStringLit( pos, charData.text )); - - // create scala.xml.Text here <: scala.xml.Node - def makeProcInstr( pos: int, procInstr:scala.xml.ProcInstr ):Tree = - procInstr.text match { - case Some(txt) => - makeProcInstr( pos, - gen.mkStringLit( pos, procInstr.target ), - makeSome( pos, gen.mkStringLit( pos, txt ))); - case _ => - makeProcInstr( pos, - gen.mkStringLit( pos, procInstr.target ), - makeNone( pos )); - } - - def makeNone( pos: int ):Tree = _scala_None( pos ); - - def makeSome( pos: int, txt:Tree ):Tree = { - val constr = make.Apply( pos, - _scala_Some( pos ), - Predef.Array[Tree] ( txt )); - make.New( pos, constr ); - } - - def makeNodeBuffer( pos: int ):Tree = { - val constr = make.Apply( pos, - _scala_xml_NodeBuffer( pos ), - Tree.EMPTY_ARRAY); - make.New( pos, constr ); - } - - def makeCharData( pos: int, txt:Tree ):Tree = { - val constr = make.Apply( pos, - _scala_xml_CharData( pos ), - Predef.Array[Tree] ( txt )); - make.New( pos, constr ); - } - - def makeComment( pos: int, txt:Tree ):Tree = { - val constr = make.Apply( pos, - _scala_xml_Comment( pos ), - Predef.Array[Tree] ( txt )); - make.New( pos, constr ); - } - - def makeProcInstr( pos: int, target:Tree, txt:Tree ):Tree = { - val constr = make.Apply( pos, - _scala_xml_ProcInstr( pos ), - Predef.Array[Tree] ( target, txt )); - make.New( pos, constr ); - } - - - def makeXMLpat(pos:int, n:Name, args:Array[Tree]):Tree = - mkXML(pos, true, new Tree$Ident( Names.PATTERN_WILDCARD ), gen.mkStringLit( pos, n.toString() ), args); - - def makeXML(pos:int, n:Name, args:Array[Tree]):Tree = { - var s = n.toString(); - val i = n.indexOf(':'); - var pref = ""; - if( i > -1 ) { - pref = s.substring( 0, i ); - s = s.substring( i, s.length() ); - } - mkXML(pos, false, gen.mkStringLit(pos, pref.toString()), gen.mkStringLit(pos, n.toString()), args); - } - def convertToText(isPattern:Boolean, t:Tree):Tree = t match { - case _:Tree$Literal => makeText(t.pos, isPattern, t); - case _ => t - } - - def convertToText( isPattern:Boolean, ts:Array[Tree] ):Array[Tree] = { - var res:Array[Tree] = null; - var i = 0; while( i < ts.length ) { - val t1 = ts( i ); - val t2 = convertToText( isPattern, t1 ); - if (!t1.eq(t2)) { - if( null == res ) { // lazy copy - res = new Array[Tree]( ts.length ); - System.arraycopy( ts, 0, res, 0, i ); - } - res( i ) = t2; - } - i = i + 1; - } - if( null == res ) ts else res; - } - - def isEmptyText(t:Tree) = t match { - case Tree$Literal(atree.AConstant.STRING("")) => true; - case _ => false; - } - def makeXMLseq( pos:int, args:Array[Tree] ) = { - val ts = new TreeList(); - //val blocArr = new Array[Tree] ( 1 + args.length ); - //val constr = _scala_collection_mutable_ArrayBuffer( pos ); - val _buffer = makeNodeBuffer( pos ); - val n = p.fresh(); - val nIdent = make.Ident(pos, n); - //blocArr( 0 ) - ts.append( make.ValDef(pos, - 0, n, Tree.Empty, - _buffer)); - - var i = 0; while( i < args.length ) { - val ipos = args(i).pos; - if( !isEmptyText( args( i ))) { - ts.append( - make.Apply( ipos, - make.Select( ipos, nIdent, _plus /*_append*/ ), - Predef.Array[Tree]( convertToText(false, args( i ) ))) - ) - } - i = i + 1; - } - make.Block( pos, ts.toArray(), nIdent ); - } - - def makeXMLseqPat( pos:int, args:Array[Tree] ) = { - make.Apply( pos, _scala_Seq( pos ), args ); - } - - /** @todo: create map here directly */ - def makeXML(pos:int,n:Name,args:Array[Tree],attrMap:ListMap[Name,Tree]):Tree={ - var t = makeXML( pos, n, args ); - if( attrMap.isEmpty ) { - t - } else { - val attrs = new Array[Tree]( attrMap.size ); - var i = 0; - for( val Pair( key, value ) <- attrMap.elements ) { - attrs( i ) = make.Apply(pos, - _scala_Tuple2( s.pos ), - Predef.Array(gen.mkStringLit( pos, key.toString() ), - value)); - i = i + 1; - }; - make.Apply(pos, - make.Select( pos, t, Names.PERCENT ), - Predef.Array( make.Apply(pos, _scala(s.pos, Names.List), - attrs))); - } - } - /** xLiteral = xExpr { xExpr } * @return Scala representation of this xml literal * precondition: s.xStartsXML == true @@ -389,7 +47,7 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { if( s.xStartsXML ) { val ts = new myTreeList(); ts.append( tree ); while( s.xStartsXML ) { ts.append( xExpr ); s.nextToken(); } - tree = makeXMLseq( pos, ts.toArray() ); + tree = mk.makeXMLseq( pos, ts.toArray() ); } tree } @@ -401,9 +59,9 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { * | `{` scalablock `}` */ def xAttributes = { - var aMap = ListMap.Empty[Name,Tree]; + var aMap = new ListMap[String, Tree]; while( xml.Parsing.isNameStart( s.ch )) { - val key = s.xName; + val key = s.xName.toString(); s.xEQ; val delim = s.ch; val value:Tree = s.ch match { @@ -440,7 +98,7 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { val aMap = if(xml.Parsing.isNameStart( s.ch )) { xAttributes; } else { - ListMap.Empty[Name,Tree]; + ListMap.Empty[String,Tree]; } Tuple2( elemName, aMap ); } @@ -448,7 +106,8 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { /* [42] '<' xmlEndTag ::= '<' '/' Name S? '>' */ def xEndTag( n:Name ) = { s.xToken('/'); - if(n != s.xName) s.xSyntaxError( "expected closing tag of " + n ); + val m = s.xName; + if(n != m) s.xSyntaxError( "expected closing tag of " + n/* +", not "+m*/); s.xSpaceOpt; s.xToken('>') } @@ -467,11 +126,11 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { */ def xExpr:Tree = { var pos = s.pos; - val Tuple2(elemName, attrMap) = xTag; + val Tuple2(qname:Name, attrMap:ListMap[String,Tree]) = xTag; if(s.ch == '/') { // empty element s.xToken('/'); s.xToken('>'); - makeXML( pos, elemName, Tree.EMPTY_ARRAY, attrMap ); + mk.makeXML( pos, qname, attrMap, Tree.EMPTY_ARRAY ); } else { // handle content s.xToken('>'); val ts = new myTreeList(); @@ -489,12 +148,12 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { case '!' => s.xNext; if( '[' == s.ch ) // CDATA - ts.append( makeCharData( pos, s.xCharData )); + ts.append( mk.CharData( pos, s.xCharData )); else // comment - ts.append( makeComment( pos, s.xComment )); + ts.append( mk.Comment( pos, s.xComment )); case '?' => // PI s.xNext; - ts.append( makeProcInstr( pos, s.xProcInstr )); + ts.append( mk.ProcInstr( pos, s.xProcInstr )); case _ => ts.append( xExpr ); // child } @@ -504,7 +163,7 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { } else { val str = new StringBuffer("{"); str.append( s.xText ); - appendTrimmed( pos, ts, str.toString() ) + mk.appendTrimmed( pos, mode, ts, str.toString() ) } // postcond: s.xScalaBlock == false! case '&' => // EntityRef or CharRef @@ -512,24 +171,23 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { s.ch match { case '#' => // CharacterRef s.xNext; - val theChar = makeText( s.pos, false, s.xCharRef ); + val theChar = mk.makeText( s.pos, false, s.xCharRef ); s.xToken(';'); ts.append( theChar ); case _ => // EntityRef - val pos = s.pos; val n = s.xName ; s.xToken(';'); - ts.append( makeEntityRef( s.pos, n )); + ts.append( mk.EntityRef( pos, n )); } case _ => // text content - appendTrimmed( s.pos, ts, s.xText ); + mk.appendTrimmed( pos, mode, ts, s.xText ); // here s.xScalaBlock might be true } } } - xEndTag( elemName ); - makeXML( pos, elemName, ts.toArray(), attrMap ); + xEndTag( qname ); + mk.makeXML( pos, qname, attrMap, ts.toArray() ); } } @@ -551,7 +209,7 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { if( s.xStartsXML ) { val ts = new myTreeList(); ts.append( tree ); while( s.xStartsXML ) { ts.append( xPattern ); s.nextToken(); } - tree = makeXMLseqPat( pos, ts.toArray() ); + tree = mk.makeXMLseqPat( pos, ts.toArray() ); } mode = oldMode; tree @@ -575,12 +233,12 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { def xPattern:Tree = { //Console.println("xPattern"); val pos = s.pos; - val elemName = s.xName; + val qname = s.xName; s.xSpaceOpt; if( s.ch == '/' ) { // empty tag s.xNext; s.xToken('>'); - return makeXMLpat( pos, elemName, Tree.EMPTY_ARRAY ); + return mk.makeXMLpat( pos, qname, Tree.EMPTY_ARRAY ); }; // else: tag with content @@ -608,13 +266,13 @@ class MarkupParser( unit:Unit, s:Scanner, p:Parser, preserveWS:boolean ) { // postcond: s.xScalaBlock = false; if( s.xScalaBlock ) throw new ApplicationError(); // assert case _ => // text - appendTrimmed( pos, ts, s.xText ); + mk.appendTrimmed( pos, mode, ts, s.xText ); // here s.xScalaBlock might be true; //if( s.xScalaBlock ) throw new ApplicationError("after:"+text); // assert } } - xEndTag( elemName ); - makeXMLpat( pos, elemName, ts.toArray() ); + xEndTag( qname ); + mk.makeXMLpat( pos, qname, ts.toArray() ); } } /* class MarkupParser */ diff --git a/sources/scala/tools/scalac/ast/parser/Scanner.scala b/sources/scala/tools/scalac/ast/parser/Scanner.scala index 0efbe8ca2c..b810ea1a0b 100644 --- a/sources/scala/tools/scalac/ast/parser/Scanner.scala +++ b/sources/scala/tools/scalac/ast/parser/Scanner.scala @@ -998,12 +998,12 @@ class Scanner(_unit: Unit) extends TokenData { * @param endch either ' or " */ def xAttributeValue( endch:char ):String = { - cbuf.setLength( 0 ); while ( ch != endch ) { putChar( ch ); xNext; }; val s = cbuf.toString(); + cbuf.setLength( 0 ); // @todo: normalize attribute value // well-formedness constraint if( s.indexOf('<') != -1 ) { diff --git a/sources/scala/tools/scalac/ast/parser/SymbolicXMLBuilder.scala b/sources/scala/tools/scalac/ast/parser/SymbolicXMLBuilder.scala new file mode 100644 index 0000000000..ca799d2fa2 --- /dev/null +++ b/sources/scala/tools/scalac/ast/parser/SymbolicXMLBuilder.scala @@ -0,0 +1,453 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002-2004, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +import scalac.ast._; +import scalac.atree.AConstant; +import scalac._; +import scalac.symtab.Modifiers; +import scala.tools.util.Position; +import java.lang.{Integer, Long, Float, Double}; +import scala.Iterator; +import scala.tools.scalac.util.NewArray; +import scala.collection.immutable.ListMap ; +import scala.collection.mutable.Buffer; +import scala.xml.{Text,TextBuffer}; +import scalac.util.{ Name, Names, TypeNames } ; +package scala.tools.scalac.ast.parser { + +/** this class builds instance of Tree that represent XML */ +class SymbolicXMLBuilder(make: TreeFactory, gen: TreeGen, p: Parser, preserveWS: Boolean ) { + + import scala.tools.scalac.ast.{TreeList => myTreeList} + + val _ArrayBuffer = Name.fromString("ArrayBuffer"); + val _Attribute = Name.fromString("Attribute"); + val _NodeBuffer = Name.fromString("NodeBuffer"); + val _NoAttributes = Name.fromString("NoAttributes"); + val _TreeMap = Name.fromString("TreeMap"); + val _Elem = Name.fromString("Elem"); + val _Seq = Name.fromString("Seq"); + val _String = Name.fromString("String"); + val _immutable = Name.fromString("immutable"); + val _mutable = Name.fromString("mutable"); + val _append = Name.fromString("append"); + val _plus = Name.fromString("$plus"); + val _collection = Name.fromString("collection"); + val _xml = Name.fromString("xml"); + val _Comment = Name.fromString("Comment"); + val _CharData = Name.fromString("CharData"); + val _Node = Name.fromString("Node"); + val _None = Name.fromString("None"); + val _Some = Name.fromString("Some"); + val _ProcInstr = Name.fromString("ProcInstr"); + val _Text = Name.fromString("Text"); + val _EntityRef = Name.fromString("EntityRef"); + + // convenience methods + private def _scala( pos: int, name: Name ) = + make.Select( pos, make.Ident( pos, Names.scala ), name ); + + private def _scala_Seq( pos: int ) = + p.convertToTypeId( _scala( pos, _Seq )); + + + private def _scala_None( pos: int ) = + _scala( pos, _None ) ; + + private def _scala_Some( pos: int ) = + p.convertToConstr( _scala( pos, _Some )); + + + private def _string( pos: int ) = + p.convertToTypeId( make.Ident( pos, _String ) ); + + private def _scala_xml( pos: int, name: Name ) = + make.Select( pos, _scala( pos, _xml ), name ); + + private def _scala_xml_Node( pos: int ) = + _scala_xml( pos, _Node ); + + private def _scala_xml_Node_NoAttributes( pos: int ) = + make.Select( pos, _scala_xml( pos, _Node ), _NoAttributes ); + + private def _scala_xml_NodeBuffer( pos: int ) = + p.convertToConstr( _scala_xml( pos, _NodeBuffer )); + + private def _scala_xml_EntityRef( pos: int ) = + p.convertToConstr( _scala_xml( pos, _EntityRef )); + + private def _scala_xml_Comment( pos: int ) = + p.convertToConstr( _scala_xml( pos, _Comment )); + + private def _scala_xml_CharData( pos: int ) = + p.convertToConstr( _scala_xml( pos, _CharData )); + + private def _scala_xml_ProcInstr( pos: int ) = + p.convertToConstr( _scala_xml( pos, _ProcInstr )); + + private def _scala_xml_Text( pos: int ) = + _scala_xml( pos, _Text ); + + + private def _scala_collection( pos: int, name: Name ) = + make.Select( pos, _scala( pos, _collection ), name ); + + private def _scala_collection_mutable( pos: int, name: Name ) = + make.Select(pos, _scala_collection(pos, _mutable ), name); + + private def _scala_collection_immutable( pos: int, name: Name ) = + make.Select(pos, _scala_collection(pos, _immutable ), name); + + private def _scala_collection_mutable_ArrayBuffer( pos: int ) = + make.Apply( pos, + make.AppliedType(pos, + p.convertToConstr( + _scala_collection_mutable(pos, _ArrayBuffer )), + Predef.Array[Tree]( + convertToTypeId( + _scala_xml_Node( pos ) )) + ), + Tree.EMPTY_ARRAY ); + + private def _scala_collection_immutable_TreeMap( pos: int ) = + make.Apply( pos, + make.AppliedType(pos, + p.convertToConstr( + _scala_collection_immutable(pos, _TreeMap )), + Predef.Array[Tree]( + _string( pos ), + _string( pos ) + ) + ), + Tree.EMPTY_ARRAY ); + + private def _emptyMap( pos:int ) = { + make.New( pos,_scala_collection_immutable_TreeMap( pos: int )); + } + + private def _scala_Tuple2( pos:int ) = + _scala( pos, Names.Tuple2 ); + + private def _scala_xml_Elem( pos:int ) = + _scala_xml( pos, _Elem ); + + private def _scala_xml_Attribute( pos: int ) = + _scala_xml( pos, _Attribute ); + + + + /** convenience method */ + def convertToTypeId(t: Tree): Tree = t match { + case Tree$Ident(name) => + make.Ident(t.pos, name.toTypeName()) + case Tree$Select(qual, name) => + make.Select(t.pos, qual, name.toTypeName()) + case _ => + t + } + + // create scala xml tree + + /** + * @arg namespace: a Tree of type defs.STRING_TYPE + * @arg label: a Tree of type defs.STRING_TYPE + * @todo map: a map of attributes !!! + */ + + def mkXML(pos:int, isPattern:boolean, namespace:Tree, label:Tree, attrs:Array[Tree], children:Array[Tree]):Tree = { + if( isPattern ) { + val ts = new myTreeList(); + ts.append( namespace ); + ts.append( label ); + ts.append( new Tree$Ident( Names.PATTERN_WILDCARD ) ); // attributes? + ts.append( convertToTextPat( children ) ); + make.Apply(pos, + convertToTypeId( _scala_xml_Elem( pos ) ), + ts.toArray()) + } else { + val ab = new scala.collection.mutable.ArrayBuffer[Tree](); + ab + namespace; + ab + label; + if(( attrs.length ) == 0 ) + ab + _scala_xml_Node_NoAttributes( pos ) + else + ab + make.Apply(pos, + make.Select( pos, + _scala_xml_Node_NoAttributes( pos ), + Names.PERCENT ), + attrs); + if(( children.length ) > 0 ) + ab + make.Typed(pos, + makeXMLseq(pos, children ), + make.Ident(pos, TypeNames.WILDCARD_STAR)); + val arr:Array[Tree] = new Array[Tree]( ab.length ); + ab.elements.copyToArray( arr, 0 ); + make.Apply( pos, _scala_xml_Elem( pos ), arr ) + } + } + + final def EntityRef( pos:int, n:Name ) = { + val constr = make.Apply( pos, + _scala_xml_EntityRef( pos ), + Predef.Array[Tree]( gen.mkStringLit( pos, n.toString() ))); + + make.New( pos, constr ); + }; + // create scala.xml.Text here <: scala.xml.Node + def makeText( pos: int, isPattern:Boolean, txt:String ):Tree = { + //makeText( pos, isPattern, gen.mkStringLit( pos, txt )); + val txt1 = gen.mkStringLit( pos, txt ); + if( isPattern ) + makeTextPat( pos, txt1 ); + else + makeText1( pos, txt1 ); + } + + def appendTrimmed(pos: int, mode:Boolean, ts:myTreeList, txt:String) = { + var textNodes = + if( !preserveWS ) + new TextBuffer().append( txt ).toText; + else + List( Text( txt )); + + for( val t <- textNodes ) + ts.append( makeText( pos, mode, t.text )); + } + + // create scala.xml.Text here <: scala.xml.Node +/* + protected def makeText( pos: int, isPattern:Boolean, txt:Tree ):Tree = { + if( isPattern ) + makeTextPat( pos, txt ); + else + makeText1( pos, txt ); + } +*/ + // create scala.xml.Text here <: scala.xml.Node + def makeTextPat( pos: int, txt:Tree ):Tree = { + return make.Apply(pos, + p.convertToConstr( _scala_xml_Text( pos ) ), + Predef.Array[Tree]( txt )); + } + + def makeText1( pos: int, txt:Tree ):Tree = { + val constr = make.Apply(pos, + p.convertToConstr( _scala_xml_Text( pos )), + Predef.Array[Tree]( txt )); + make.New( pos, constr ); + } + + // create + def makeAttribute( pos: int, ns:String, key:String, value:Tree ):Tree = + make.Apply(pos, + _scala_xml_Attribute(pos), + Predef.Array[Tree] ( + make.Ident( pos, Name.fromString( ns )), + gen.mkStringLit( pos, key ), + value + ) + ); + + // create + def Comment( pos: int, comment:scala.xml.Comment ):Tree = + Comment( pos, gen.mkStringLit( pos, comment.text )); + + // create + def CharData( pos: int, charData:scala.xml.CharData ):Tree = + CharData( pos, gen.mkStringLit( pos, charData.text )); + + // create scala.xml.Text here <: scala.xml.Node + def ProcInstr( pos: int, procInstr:scala.xml.ProcInstr ):Tree = + procInstr.text match { + case Some(txt) => + ProcInstr( pos, + gen.mkStringLit( pos, procInstr.target ), + makeSome( pos, gen.mkStringLit( pos, txt ))); + case _ => + ProcInstr( pos, + gen.mkStringLit( pos, procInstr.target ), + makeNone( pos )); + } + + def makeNone( pos: int ):Tree = _scala_None( pos ); + + def makeSome( pos: int, txt:Tree ):Tree = { + val constr = make.Apply( pos, + _scala_Some( pos ), + Predef.Array[Tree] ( txt )); + make.New( pos, constr ); + } + + def makeNodeBuffer(pos: int): Tree = { + val constr = make.Apply( pos, + _scala_xml_NodeBuffer( pos ), + Tree.EMPTY_ARRAY); + make.New( pos, constr ); + } + + protected def CharData(pos: int, txt: Tree):Tree = { + val constr = make.Apply( pos, + _scala_xml_CharData( pos ), + Predef.Array[Tree] ( txt )); + make.New( pos, constr ); + } + + protected def Comment(pos: int, txt: Tree):Tree = { + val constr = make.Apply( pos, + _scala_xml_Comment( pos ), + Predef.Array[Tree] ( txt )); + make.New( pos, constr ); + } + + protected def ProcInstr(pos: int, target: Tree, txt: Tree): Tree = { + val constr = make.Apply( pos, + _scala_xml_ProcInstr( pos ), + Predef.Array[Tree] ( target, txt )); + make.New( pos, constr ); + } + + /** @todo: attributes */ + def makeXMLpat(pos: int, n: Name, args: Array[Tree]): Tree = + mkXML(pos, + true, + new Tree$Ident( Names.PATTERN_WILDCARD ):Tree, + gen.mkStringLit( pos, n.toString() ):Tree, + Tree.EMPTY_ARRAY:Array[Tree], + args:Array[Tree]); + + protected def convertToTextPat(t: Tree): Tree = t match { + case _:Tree$Literal => makeTextPat(t.pos, t); + case _ => t + } + + protected def convertToTextPat(ts: Array[Tree]): Array[Tree] = { + var res:Array[Tree] = null; + var i = 0; while( i < ts.length ) { + val t1 = ts( i ); + val t2 = convertToTextPat( t1 ); + if (!t1.eq(t2)) { + if( null == res ) { // lazy copy + res = new Array[Tree]( ts.length ); + System.arraycopy( ts, 0, res, 0, i ); + } + res( i ) = t2; + } + i = i + 1; + } + if( null == res ) ts else res; + } + + def isEmptyText(t:Tree) = t match { + case Tree$Literal(atree.AConstant.STRING("")) => true; + case _ => false; + } + def makeXMLseq( pos:int, args:Array[Tree] ) = { + val ts = new TreeList(); + //val blocArr = new Array[Tree] ( 1 + args.length ); + //val constr = _scala_collection_mutable_ArrayBuffer( pos ); + val _buffer = makeNodeBuffer( pos ); + val n = p.fresh(); + val nIdent = make.Ident(pos, n); + //blocArr( 0 ) + ts.append( make.ValDef(pos, + 0, n, Tree.Empty, + _buffer)); + + var i = 0; while( i < args.length ) { + val ipos = args(i).pos; + if( !isEmptyText( args( i ))) { + ts.append( + make.Apply( ipos, + make.Select( ipos, nIdent, _plus /*_append*/ ), + Predef.Array[Tree]( args( i ) )) + ) + } + i = i + 1; + } + make.Block( pos, ts.toArray(), nIdent ); + } + + def makeXMLseqPat( pos:int, args:Array[Tree] ) = { + make.Apply( pos, _scala_Seq( pos ), args ); + } + + + + def getPrefix( name:String ):Option[String] = { + val i = name.indexOf(':'); + if( i != -1 ) Some( name.substring(0, i) ) else None + } + + def qualified( pos:Int, name:String ):Pair[String,String] = + getPrefix( name ).match { + case Some( pref ) => + val newLabel = name.substring( pref.length(), name.length() ); + // if( newLabel.indexOf(':') != -1 ) syntaxError + Pair( "namespace$"+pref, newLabel ); + case None => + Pair( "namespace$default", name ); + } + + /** makes an element */ + def makeXML(pos: int, labeln: Name, attrMap: ListMap[String,Tree], args: Array[Tree]): Tree={ + var label = labeln.toString(); + var setNS = ListMap.Empty[String, Tree]; + + + for( val z <- attrMap.keys; z.startsWith("xmlns") ) { + val i = z.indexOf(':'); + if( i == -1 ) + setNS = setNS.update("default", attrMap( z )); + else { + val zz = z.substring( i+1, z.length() ); + setNS = setNS.update( zz, attrMap( z )); + } + } + val i = label.indexOf(':'); + val Pair( namespace, newlabel ) = qualified( pos, label ); + + var attr:Array[Tree] = + if( attrMap.isEmpty ) + Tree.EMPTY_ARRAY + else { + val attrs:Array[Tree] = new Array[Tree](attrMap.size); + var k = 0; + var it = attrMap.elements; + while( it.hasNext ) { + val ansk = it.next; + val Pair( ns, aname ) = qualified( pos, ansk._1 ); + attrs( k ) = makeAttribute( pos, ns, aname, ansk._2 ); + k = k + 1; + } + attrs + } + + var t = mkXML(pos, + false, + make.Ident(pos, Name.fromString(namespace)):Tree, + gen.mkStringLit(pos, newlabel):Tree, + attr:Array[Tree], + args:Array[Tree]); + if( !setNS.isEmpty ) { + val nsStms = new Array[Tree]( setNS.size ); + var i = 0; + for( val Pair(ns:String, uri:Tree) <- setNS.toList ) { + nsStms( i ) = setNamespacePrefix(pos, ns, uri ); + i = i + 1; + } + make.Block( pos, nsStms, t ) + } else + t + } + + def setNamespacePrefix(pos:Int, pref:String, uri:Tree) = + make.ValDef(pos, 0, Name.fromString("namespace$"+pref), Tree.Empty, uri); + + +} +} diff --git a/sources/scala/xml/Attribute.scala b/sources/scala/xml/Attribute.scala new file mode 100644 index 0000000000..92eeedf93c --- /dev/null +++ b/sources/scala/xml/Attribute.scala @@ -0,0 +1,29 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml ; + +/** An XML attribute + * + * @todo allow for attributes that are not strings + * + * @param namespace$$ the namespace URI + * @param key$$ the local attribute name + * @author Burak Emir + */ +case class Attribute(namespace:String, key:String, value:String) with Ordered[Attribute] { + + def compareTo [b >: Attribute <% Ordered[b]](that: b): int = that match { + case z:Attribute => + val i = key.compareTo( z.key ); + if( i != 0 ) i else namespace.compareTo( z.namespace ) + case _ => -(that.compareTo(this)); + } + +} diff --git a/sources/scala/xml/AttributeSeq.scala b/sources/scala/xml/AttributeSeq.scala new file mode 100644 index 0000000000..90d535cc96 --- /dev/null +++ b/sources/scala/xml/AttributeSeq.scala @@ -0,0 +1,69 @@ +/* __ *\ +** ________ ___ / / ___ Scala API ** +** / __/ __// _ | / / / _ | (c) 2003-2004, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +** $Id$ +\* */ + +package scala.xml ; + +import scala.collection.mutable.HashMap ; +import scala.collection.immutable.TreeSet ; + +object AttributeSeq { + final val Empty = new AttributeSeq(); + + final def fromHashMap(as:HashMap[Pair[String,String],String]) = { + new AttributeSeq( { + for( val a <- as.keys.toList ) + yield Attribute(a._1,a._2, as(a)) + }:_* ) + } +} + +/** Sorted linear list of XML attributes. + * An attribute seq does *not* contain namespace defining attributes + * like xmlns or xmlns:pref + * @author Burak Emir + */ +class AttributeSeq( as:Attribute* ) with Seq[Attribute] { + + private var treeSet:TreeSet[Attribute] = new TreeSet[Attribute]; + + for( val a <- as ) { + if( a.key != "xmlns" ) { + treeSet = treeSet + a ; + } + } + final def length = treeSet.size; + final def elements = treeSet.elements; + final def apply(i:Int) = treeSet.elements.drop(i).next; + + def lookup(ns:String, key:String):Option[Attribute] = { + val it = treeSet.elements; + while( it.hasNext ) { + val a = it.next; + if( a.key > key ) return None + else if( a.key == key ) { + if( a.namespace > ns ) return None + else if( a.namespace == ns ) return Some(a) + } + } + return None; + } + + /** Return a new AttributeSeq with updated or added attributes + * + * @param attrs + * @return a new symbol with updated attributes + */ + final def %(attrs: Attribute*) = + new AttributeSeq((elements.toList ::: attrs.elements.toList):_*); + + final def map(f: Attribute => Attribute): AttributeSeq = { + new AttributeSeq( elements.map( f ).toList:_* ) + } +} + diff --git a/sources/scala/xml/BindingFactoryAdapter.scala b/sources/scala/xml/BindingFactoryAdapter.scala index 3c04e4fb49..7922a060ef 100644 --- a/sources/scala/xml/BindingFactoryAdapter.scala +++ b/sources/scala/xml/BindingFactoryAdapter.scala @@ -21,7 +21,7 @@ abstract class BindingFactoryAdapter extends FactoryAdapter() { /** mapping from element names to an element constructor * (constr:Seq[Node],HashMap[String,String] => Node) */ - val f: Map[ String, (HashMap[String,String],Seq[Node]) => Node ]; + val f: Map[ String, (AttributeSeq,Seq[Node]) => Node ]; /** mapping from element names to a truth value indicating * whether the corresponding element may have text children @@ -41,16 +41,16 @@ abstract class BindingFactoryAdapter extends FactoryAdapter() { /** creates an element. see also compress */ def createNode(uri:String, elemName:String, - attribs:HashMap[String,String], + attribs:HashMap[Pair[String,String],String], children:List[Node] ):Node = { val uri$ = uri.intern(); - + val attribs1 = AttributeSeq.fromHashMap(attribs); // 2do:optimize if( !compress ) { // get constructor val c = f( elemName ); - c( attribs, children ); + c( attribs1, children ); } else { // do hash-consing val ahc = attribs.toList.hashCode(); @@ -65,7 +65,7 @@ abstract class BindingFactoryAdapter extends FactoryAdapter() { case None => // get constructor val c = f( elemName ); - val el = c( attribs, children ); + val el = c( attribs1, children ); cache.update( h, el ); el } diff --git a/sources/scala/xml/CharData.scala b/sources/scala/xml/CharData.scala index 19e12d88f3..287bcdc390 100644 --- a/sources/scala/xml/CharData.scala +++ b/sources/scala/xml/CharData.scala @@ -16,7 +16,7 @@ import scala.collection.immutable ; * @param text text contained in this node, may not contain "]]>" **/ -case class CharData( text:String ) extends Node { +case class CharData( text:String ) extends SpecialNode { final override def typeTag$:Int = -4; @@ -27,15 +27,6 @@ case class CharData( text:String ) extends Node { */ def label = "#CDATA"; - /** always Node.EmptyNamespace */ - final def namespace = Node.EmptyNamespace; - - /** always empty */ - final def attribute = Node.NoAttributes; - - /** always empty */ - final def child = Nil; - /** returns "<![CDATA["+text+"]]>" */ final override def toString() = ""; diff --git a/sources/scala/xml/Elem.scala b/sources/scala/xml/Elem.scala index 074f76d503..4150eb922d 100644 --- a/sources/scala/xml/Elem.scala +++ b/sources/scala/xml/Elem.scala @@ -9,7 +9,7 @@ package scala.xml; -import scala.collection.immutable; +import scala.collection.mutable.ArrayBuffer; /** The case class Elem implements the Node trait, * providing an immutable data object representing an XML element. @@ -20,7 +20,7 @@ import scala.collection.immutable; * @param child the children of this node * @author Burak Emir */ -case class Elem( namespace$$:String, label$$: String, attribute:immutable.Map[String,String], child: Node*) extends Node { +case class Elem( namespace$$:String, label$$: String, attributes: AttributeSeq, child: Node*) extends Node { final val namespaceIntern = namespace$$.intern(); final def namespace = namespaceIntern; @@ -41,15 +41,9 @@ case class Elem( namespace$$:String, label$$: String, attribute:immutable.Map[St * @param attrs * @return a new symbol with updated attributes */ - final def %(attrs: Seq[Pair[String, String]]): Elem = { - var newmap = new immutable.TreeMap[String, String](); - for ( val p <- attribute.elements ) { - newmap = newmap.update( p._1, p._2 ) - } - for ( val p <- attrs ) { - newmap = newmap.update( p._1, p._2 ) - } - Elem(namespace, label, newmap, child:_*) + final def %(attrs: Seq[Attribute]): Elem = { + val aseq = new AttributeSeq((attributes.toList ::: attrs.toList):_*); + Elem(namespace, label, aseq, child:_*) } /** Return a new symbol with updated attribute @@ -57,13 +51,9 @@ case class Elem( namespace$$:String, label$$: String, attribute:immutable.Map[St * @param attr * @return a new symbol with updated attribute */ - final def %(attr: Pair[String, String]): Elem = { - var newmap = new immutable.TreeMap[String, String](); - for ( val p <- attribute.elements ) { - newmap = newmap.update( p._1, p._2 ) - } - newmap = newmap.update( attr._1, attr._2 ); - Elem(namespace, label, newmap, child:_*) + final def %(attr: Attribute): Elem = { + val aseq = new AttributeSeq((attributes.toList ::: attr :: Nil):_*); + Elem(namespace, label, aseq, child:_*) } } diff --git a/sources/scala/xml/FactoryAdapter.scala b/sources/scala/xml/FactoryAdapter.scala index b9f8fc3d6b..386b4b8841 100644 --- a/sources/scala/xml/FactoryAdapter.scala +++ b/sources/scala/xml/FactoryAdapter.scala @@ -37,7 +37,7 @@ import javax.xml.parsers.SAXParser; abstract class FactoryAdapter extends DefaultHandler() { val buffer = new StringBuffer(); - val attribStack = new Stack[HashMap[String,String]]; + val attribStack = new Stack[HashMap[Pair[String,String],String]]; val hStack = new Stack[Node]; // [ element ] contains siblings val tagStack = new Stack[String]; // [String] @@ -59,7 +59,7 @@ abstract class FactoryAdapter extends DefaultHandler() { */ def createNode(uri:String, elemName:String , - attribs:HashMap[String,String] , + attribs:HashMap[Pair[String,String],String] , chIter:List[Node] ):Node; //abstract /** creates a Text node. @@ -127,21 +127,22 @@ abstract class FactoryAdapter extends DefaultHandler() { capture = nodeContainsText(localName) ; hStack.push( null ); - var map:HashMap[String,String] = null:HashMap[String,String]; + var map:HashMap[Pair[String,String],String] = null:HashMap[Pair[String,String],String]; if (attributes == null) { // may not happen } else { - map = new HashMap[String,String]; + map = new HashMap[Pair[String,String],String]; for( val i <- List.range( 0, attributes.getLength() )) { + val attrNS = attributes.getURI(i); val attrLocalName = attributes.getLocalName(i); val attrType = attributes.getType(i); val attrValue = attributes.getValue(i); // we only handle string attributes if( attrType.equals("CDATA") ) { - map.update( attrLocalName, attrValue ); + map.update( Pair(attrNS,attrLocalName), attrValue ); } } } diff --git a/sources/scala/xml/Namespace.scala b/sources/scala/xml/Namespace.scala deleted file mode 100644 index 0643deb9b8..0000000000 --- a/sources/scala/xml/Namespace.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -** $Id$ -\* */ -package scala.xml ; - -/** an XML namespace URI */ -case class Namespace( uri:String ) { - final val code = NamespaceRegistry.getCode( uri ); -} diff --git a/sources/scala/xml/NamespaceRegistry.scala b/sources/scala/xml/NamespaceRegistry.scala deleted file mode 100644 index 5b6ba348cd..0000000000 --- a/sources/scala/xml/NamespaceRegistry.scala +++ /dev/null @@ -1,43 +0,0 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2002-2004, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -** $Id$ -\* */ -package scala.xml ; - -import scala.collection.mutable.HashMap; -import scala.collection.immutable.TreeMap; - -/** thread-safe storage of namespace URIs. */ -object NamespaceRegistry { - - final var next = 0; - /* hashmap from namespaces to ints */ - final var nmap: HashMap[String, Int] = new HashMap(); - final var dmap: TreeMap[Int,String] = new TreeMap(); - - private def newCode( uri:String ):Int = { - val d = next; - next = next + 1; - nmap( uri ) = d; - dmap( d ) = uri; - d - } - - /* returns a code for the argument namespace uri. Registers it if needed */ - def getCode(uri: String):Int = synchronized { - val c = nmap.get( uri ); - if( c.isEmpty ) newCode( uri ) else c.get - } - - def getNamespace( i:Int ):Namespace = synchronized { - Namespace( dmap( i ) ); - } - - /* empty namespace has code 0 */ - getCode(""); - -} diff --git a/sources/scala/xml/Node.scala b/sources/scala/xml/Node.scala index 0c129a314d..639c145122 100644 --- a/sources/scala/xml/Node.scala +++ b/sources/scala/xml/Node.scala @@ -14,9 +14,8 @@ import scala.collection.immutable ; object Node { - /** the constant empty attribute map */ - val NoAttributes: immutable.TreeMap[String,String] = - immutable.TreeMap.Empty[String,String]; + /** the constant empty attribute sequence */ + final def NoAttributes: AttributeSeq = AttributeSeq.Empty; /** the empty namespace */ val EmptyNamespace = ""; @@ -36,11 +35,26 @@ trait Node { /** the namespace of this node */ def namespace: String; - /** attribute axis */ - def attribute: Map[String,String] ; + /** attribute map for attributes with the same namespace as this element */ + final def attribute: Map[String,String] = new Map[String, String] { + def size = attributes.length; + def elements = + attributes.elements + .filter( x => x.namespace == namespace ) + .map( x => Pair(x.key, x.value) ); + def get(x:String) = { + attributes.lookup( namespace, x ) match { + case Some( x ) => Some( x.value ); + case _ => None + } + } + } + /** attribute axis - all attributes of this node, in order defined by attrib + */ + def attributes: AttributeSeq; /** child axis (all children of this node) */ - def child: Seq[Node]; + def child: Seq[Node]; /** descendant axis (all descendants of this node) */ def descendant:List[Node] = child.toList.flatMap { diff --git a/sources/scala/xml/PrettyPrinter.scala b/sources/scala/xml/PrettyPrinter.scala index b6a6e4a525..d74751c4fd 100644 --- a/sources/scala/xml/PrettyPrinter.scala +++ b/sources/scala/xml/PrettyPrinter.scala @@ -31,17 +31,18 @@ class PrettyPrinter( width:Int, step:Int ) { case class Box( col:Int, s:String ) extends Item; case class Para( s:String ) extends Item; - var items:List[Item] = Nil; + protected var items:List[Item] = Nil; - var cur = 0; + protected var cur = 0; + protected var pmap:Map[String,String] = _; - def reset() = { + protected def reset() = { cur = 0; items = Nil; } /* try to cut at whitespace */ - def cut( s:String, ind:Int ):List[Item] = { + protected def cut( s:String, ind:Int ):List[Item] = { val tmp = width - cur; if( s.length() < tmp ) return List(Box(ind,s)); @@ -67,7 +68,7 @@ class PrettyPrinter( width:Int, step:Int ) { } /** try to make indented box, if possible, else para */ - def makeBox( ind:Int, s:String ) = { + protected def makeBox( ind:Int, s:String ) = { if( cur < ind ) cur == ind; if( cur + s.length() > width ) { // fits in this line @@ -82,44 +83,48 @@ class PrettyPrinter( width:Int, step:Int ) { } // dont respect indent in para, but afterwards - def makePara( ind:Int, s:String ) = { + protected def makePara( ind:Int, s:String ) = { items = Break::Para( s )::Break::items; cur = ind; } // respect indent - def makeBreak() = { // using wrapping here... + protected def makeBreak() = { // using wrapping here... items = Break::items; cur = 0; } - def leafTag( n:Node ) = { + protected def leafTag( n:Node ) = { val sb = new StringBuffer("<"); - sb.append( n.label ); - Utility.attr2xml( n.attribute.elements, sb ); + Utility.appendPrefixedName( n.namespace, n.label, pmap, sb ); + Utility.attr2xml( n.attributes.elements, pmap, sb ); sb.append("/>"); sb.toString(); } - def startTag( n:Node ) = { + protected def startTag(n: Node) = { val sb = new StringBuffer("<"); - sb.append( n.label ); - Utility.attr2xml( n.attribute.elements, sb ); + Utility.appendPrefixedName( n.namespace, n.label, pmap, sb ); + Utility.attr2xml( n.attributes.elements, pmap, sb ); sb.append('>'); sb.toString(); } - /* returns a formatted string containing well-formed XML - **/ - def format( n:Node ):String = { + /** appends a formatted string containing well-formed XML with + * given namespace to prefix mapping to the given stringbuffer + * @param n the node to be serialized + * @param pmap the namespace to prefix mapping + * @param sb the stringbuffer to append to + */ + def format(n: Node, pmap: Map[String,String], sb: StringBuffer ): Unit = { reset(); + this.pmap = pmap; traverse( n, 0 ); - val sb = new StringBuffer(); var cur = 0; //Console.println( items.reverse ); for( val b <- items.reverse ) b match { case Break => - sb.append('\n'); // on windows: \r\n + sb.append('\n'); // on windows: \r\n ? cur = 0; case Box(i, s) => while( cur < i ) { @@ -130,20 +135,9 @@ class PrettyPrinter( width:Int, step:Int ) { case Para( s ) => sb.append( s ); } - sb.toString(); - } - - /* returns a formatted string containing well-formed XML nodes. - **/ - def format( ns:Seq[Node] ):String = { - var sb2 = new StringBuffer(); - for( val n <- ns.elements ) { - sb2.append( format( n )) - } - sb2.toString(); } - def breakable( n:Node ):boolean = { + protected def breakable( n:Node ):boolean = { val it = n.child.elements; while( it.hasNext ) it.next match { @@ -153,7 +147,7 @@ class PrettyPrinter( width:Int, step:Int ) { return false } /** @param tail: what we'd like to sqeeze in */ - def traverse( node:Node, ind:int ):Unit = { + protected def traverse( node:Node, ind:int ):Unit = { node match { case _:Text | _:CharData | _:Comment | _:EntityRef | _:ProcInstr => @@ -191,11 +185,57 @@ class PrettyPrinter( width:Int, step:Int ) { } } - def traverse( it:Iterator[Node], ind:int ):unit = { + protected def traverse( it:Iterator[Node], ind:int ):unit = { for( val c <- it ) { traverse( c, ind ); makeBreak(); } } + // public convenience methods + + /** returns a formatted string containing well-formed XML with + * default namespace prefix mapping + * @param n the node to be serialized + */ + def format(n: Node): String = format(n, Utility.defaultPrefixes( n )); + + /** returns a formatted string containing well-formed XML with + * given namespace to prefix mapping + * @param n the node to be serialized + * @param pmap the namespace to prefix mapping + */ + def format(n: Node, pmap: Map[String,String]): String = { + val sb = new StringBuffer(); + format( n, pmap, sb ); + sb.toString(); + } + + /* returns a formatted string containing well-formed XML nodes with + * default namespace prefix mapping + */ + def format( nodes:Seq[Node] ):String = { + format(nodes, Utility.defaultPrefixes( nodes )) + } + + /** returns a formatted string containing well-formed XML + * @param nodes the sequence of nodes to be serialized + * @param pmap the namespace to prefix mapping + */ + def format( nodes:Seq[Node], pmap:Map[String,String] ):String = { + var sb = new StringBuffer(); + format( nodes, pmap, sb ); + sb.toString(); + } + + /** appends a formatted string containing well-formed XML with + * the given namespace to prefix mapping to the given stringbuffer + * @param n the node to be serialized + * @param pmap the namespace to prefix mapping + * @param sb the string buffer to which to append to + */ + def format( nodes: Seq[Node], pmap: Map[String,String], sb: StringBuffer ): Unit = { for( val n <- nodes.elements ) { + sb.append(format( n, pmap )) + } + } } diff --git a/sources/scala/xml/SpecialNode.scala b/sources/scala/xml/SpecialNode.scala index ad3408641a..974ee4ddd9 100644 --- a/sources/scala/xml/SpecialNode.scala +++ b/sources/scala/xml/SpecialNode.scala @@ -18,7 +18,7 @@ abstract class SpecialNode extends Node { final def namespace = Node.EmptyNamespace; /** always empty */ - final def attribute = Node.NoAttributes; + final def attributes = Node.NoAttributes; /** always empty */ final def child = Nil; diff --git a/sources/scala/xml/Utility.scala b/sources/scala/xml/Utility.scala index 17555941d5..2687d32121 100644 --- a/sources/scala/xml/Utility.scala +++ b/sources/scala/xml/Utility.scala @@ -10,6 +10,8 @@ package scala.xml ; import java.lang.StringBuffer ; /* Java dependency! */ +import scala.collection.mutable ; +import scala.collection.immutable ; import scala.collection.Map ; /** Utility functions for processing instances of bound and not bound XML @@ -35,40 +37,151 @@ object Utility { s.toString(); } + /** returns a set of all namespaces appearing in a node and all its + * descendants, including the empty namespaces + */ + def collectNamespaces(node: Node): mutable.Set[String] = + collectNamespaces(node, new mutable.HashSet[String]()); + + /** returns a set of all namespaces appearing in a sequence of nodes + * and all their descendants, including the empty namespaces + */ + def collectNamespaces(nodes: Seq[Node]): mutable.Set[String] = { + var m = new mutable.HashSet[String](); + for(val n <- nodes) + collectNamespaces(n, m); + return m + } + + private def collectNamespaces(node: Node, set: mutable.Set[String]): mutable.Set[String] = { + def collect( n:Node ):Unit = { + if( n.typeTag$ >= 0 ) { + set += n.namespace; /* namespaces are interned, so hashing is fast */ + for( val a <- n.attributes ) + set += a.namespace; + for( val i <- n.child ) + collect(i); + } + } + collect(node); + return set; + } + /** a prefix mapping that maps the empty namespace to the empty prefix */ + val noPrefixes:Map[String,String] = + immutable.ListMap.Empty[String,String].update("",""); + + /** returns a default prefix mapping for a set of namespaces. + * the empty namespace is mapped to the empty prefix + */ + def defaultPrefixes(rootns:String,nset:mutable.Set[String]):Map[String,String] = { + val map = new mutable.HashMap[String,String](); + map.update( rootns, "" ); + var i = 0; + for( val ns <- nset ) + map.get( ns ) match { + case None => map.update( ns, "ns"+i ); + case Some( _ ) => + } + + return map; + } + + /** same as defaultPrefixes(n.namespace, collectNamespaces( n )) */ + def defaultPrefixes( n:Node ):Map[String,String] = + defaultPrefixes(n.namespace, collectNamespaces( n )); + + /** same as defaultPrefixes("", ncollectNamespaces( nodes )) */ + def defaultPrefixes(nodes: Seq[Node]): Map[String,String] = { + defaultPrefixes("", collectNamespaces( nodes )); + } + + /** serializes an instance of Node to a string that contains well-formed XML * @todo define a way to escape literal characters to &xx; references */ - def toXML( n:Node ):String = n match { + def toXML(n: Node): String = n match { case Text( t ) => escape( t ); case _:EntityRef | _:Comment | _:ProcInstr => n.toString(); case _ => val s = new StringBuffer(); - toXML( n, s ); + toXML( n, defaultPrefixes( n ), s ); s.toString(); } - /** serializes an instance of Node to the given stringbuffer + /** serializes a node with given namespace prefix mapping + * @arg n the node to serialize + * @pmap a mapping from namespace URIs to prefixes + */ + def toXML(n: Node, pmap:Map[String,String] ):String = n match { + case Text( t ) => + escape( t ); + case _:EntityRef | _:Comment | _:ProcInstr => + n.toString(); + case _ => + val sb = new StringBuffer(); + toXML( n, pmap, sb ); + sb.toString(); + } + + /** serializes a tree to the given stringbuffer + ** with the given namespace prefix mapping + * @param n the root node */ - def toXML( n:Node, s:StringBuffer ):Unit = n match { + def toXML( n:Node, pmap:Map[String,String], sb:StringBuffer ):Unit = n match { case Text( t ) => - s.append( escape( t ) ); + sb.append( escape( t ) ); case _:EntityRef | _:Comment | _:ProcInstr => - s.append( n.toString() ); + sb.append( n.toString() ); + case x:Node => + sb.append('<'); + appendPrefixedName( x.namespace, x.label, pmap, sb ); + if( x.attributes.length != 0 ) { + attr2xml( x.attributes.elements, pmap, sb ) + } + if( (pmap.size != 1)||pmap.get("").isEmpty) { + for( val Pair(ns,pref) <- pmap.elements ) { + sb.append(' '); + sb.append("xmlns"); + if( pref.length() > 0 ) sb.append(':'); + sb.append(pref); + sb.append("=\""); + sb.append(ns); + sb.append('"') + } + } + sb.append('>'); + for( val c <- x.child.elements ) { + toXML1( c, pmap, sb ); + } + sb.append("'); + } + + /** serializes a tree to the given stringbuffer + ** with the given namespace prefix mapping + * @param n the root node + */ + def toXML1( n:Node, pmap:Map[String,String], sb:StringBuffer ):Unit = n match { + case Text( t ) => + sb.append( escape( t ) ); + case _:EntityRef | _:Comment | _:ProcInstr => + sb.append( n.toString() ); case x:Node => { - s.append('<'); - s.append( x.label ); - if( x.attribute.size != 0 ) { - attr2xml( x.attribute.elements, s ) + sb.append('<'); + appendPrefixedName( x.namespace, x.label, pmap, sb ); + if( x.attributes.length != 0 ) { + attr2xml( x.attributes.elements, pmap, sb ) } - s.append('>'); + sb.append('>'); for( val c <- x.child.elements ) { - toXML( c, s ); + toXML1( c, pmap, sb ); } - s.append("'); + sb.append("'); } } @@ -77,50 +190,67 @@ object Utility { ch.foldLeft ("") { (s:String,n:Node) => s + n.toString() } } */ - /** for a Node n, returns string representation of n.attributes **/ - def attr2xml( attrib:Iterator[Pair[String, String]], sb:StringBuffer ):Unit = { + def attr2xml( attrib: Iterator[Attribute], pmap: Map[String, String], sb: StringBuffer ):Unit = { for( val x <- attrib ) { sb.append( " " ); - sb.append( x._1 ); + appendPrefixedName( x.namespace, x.key, pmap, sb ); sb.append("="); - val sep = { if( x._2.indexOf('"') != -1 ) '\'' else '"' }; - sb.append( sep ); - sb.append( x._2 ); - sb.append( sep ); + appendQuoted(x.value, sb) } } - - /** for a Node n, returns string representation of n.attributes **/ - def attr2xml( attrib:Iterator[Pair[String, String]] ):String = { - val t = new StringBuffer(); - attr2xml( attrib, t ); - t.toString(); - } - /** returns a hashcode for the given constituents of a node */ def hashCode(uri:String, label:String, attribHashCode:int, children:Seq[Node]) = { 41 * uri.hashCode() % 7 + label.hashCode() + attribHashCode + children.hashCode() } /** returns a hashcode for the given constituents of a node */ - def hashCode(uri:String, label:String, attribs:scala.collection.mutable.HashMap[String,String], children:Seq[Node]) = { + def hashCode(uri:String, label:String, attribs:scala.collection.mutable.HashMap[Pair[String,String],String], children:Seq[Node]) = { 41 * uri.hashCode() % 7 + label.hashCode() + attribs.toList.hashCode() + children.hashCode() } def systemLiteralToString( s:String ) = { - val ch = { - if( s.indexOf('"') != -1 ) '\'' else '"' - } - new StringBuffer("SYSTEM ").append(ch).append(s).append(ch).toString(); + val sb = new StringBuffer("SYSTEM "); + appendQuoted(s, sb); + sb.toString(); } def publicLiteralToString( s:String ) = { - val ch = { - if( s.indexOf('\'') != -1 ) '"' else '\'' + val sb = new StringBuffer("PUBLIC "); + sb.append('"').append(s).append('"'); + sb.toString(); + } + + + + def appendPrefixedName( ns: String, name: String, pmap: Map[String, String], sb: StringBuffer ):Unit = { + val pref = pmap( ns ); + if( pref.length() > 0 ) { + sb.append( pref ); + sb.append(':'); } - new StringBuffer("PUBLIC ").append(ch).append(s).append(ch).toString(); + sb.append( name ); } + /** appends "s" if s does not contain ", 's' + * otherwise + */ + def appendQuoted( s:String, sb:StringBuffer ):Unit = { + val ch = { if( s.indexOf('"') == -1 ) '"' else '\''} + sb.append(ch).append(s).append(ch); + } + + /** appends "s" and escapes and " i s with \" */ + def appendEscapedQuoted( s:String, sb:StringBuffer ):Unit = { + sb.append('"'); + val z:Seq[Char] = s; + for( val c <- z ) c match { + case '"' => sb.append('\\'); sb.append('"'); + case _ => sb.append( c ); + } + sb.append('"'); + } + + } diff --git a/sources/scala/xml/dtd/Decl.scala b/sources/scala/xml/dtd/Decl.scala index 83a8c29b29..dd0a71f435 100644 --- a/sources/scala/xml/dtd/Decl.scala +++ b/sources/scala/xml/dtd/Decl.scala @@ -36,7 +36,22 @@ case class ElemDecl( name:String , }; /** an attribute declaration */ -case class AttrDecl( name:String, tpe:String, default:DefaultDecl ) extends MarkupDecl; +case class AttrDecl( name:String, tpe:String, default:DefaultDecl ) extends MarkupDecl { + final override def toString() = { + val sb = new StringBuffer("AttrDecl("); + sb.append('"'); + sb.append( name ); + sb.append('"'); + sb.append(','); + sb.append('"'); + sb.append( tpe ); + sb.append('"'); + sb.append(','); + sb.append(default.toString()); + sb.append(')'); + sb.toString(); + } +} /** an entity declaration */ case class EntityDecl( name:String, tpe:String ) extends MarkupDecl; @@ -57,6 +72,19 @@ case class PEReference(ent:String) extends Decl { class DefaultDecl ; -case object REQUIRED, IMPLIED extends DefaultDecl; - -case class DEFAULT(fixed:boolean, attValue:String) extends DefaultDecl; +case object REQUIRED extends DefaultDecl { + final override def toString() = "REQUIRED"; +} +case object IMPLIED extends DefaultDecl { + final override def toString() = "IMPLIED"; +} +case class DEFAULT(fixed:boolean, attValue:String) extends DefaultDecl { + final override def toString() = { + val sb = new StringBuffer("DEFAULT("); + sb.append( fixed ); + sb.append(','); + Utility.appendEscapedQuoted( attValue, sb ); + sb.append(')'); + sb.toString() + } +} diff --git a/sources/scala/xml/dtd/Validation.scala b/sources/scala/xml/dtd/Validation.scala new file mode 100644 index 0000000000..b9e9fbcaba --- /dev/null +++ b/sources/scala/xml/dtd/Validation.scala @@ -0,0 +1,92 @@ +package scala.xml.dtd ; + +import scala.collection.mutable; +import scala.collection.Set; +import scala.collection.Map; + +class ValidationException( e:String ) extends Exception( e ); + +object ValidationException { + def fromFixedAttribute( k: String, value: String, actual: String ) = + new ValidationException("value of attribute " + k + " FIXED to \""+value+"\", but document tries \""+actual+"\""); + + def fromUndefinedAttribute( key:String ) = + new ValidationException("undefined attribute " + key ); + + def fromMissingAttribute( allKeys:Set[String] ) = { + new ValidationException("missing value for REQUIRED attribute"+ + { if( allKeys.size > 1 ) "s" else "" }+ + allKeys ); + } +} + +/** only CDATA attributes, ignores attributes that have different namespace +* @param: attrSpec +*/ +class AttributeValidator( namespace$$:String, attrSpec1: Seq[dtd.AttrDecl]) { + final val namespace = namespace$$.intern(); + final val attrSpec = new mutable.HashMap[String,AttrDecl](); + var attribsTemplate:List[Attribute] = Nil; + for( val adecl <- attrSpec1 ) { + attrSpec.update( adecl.name, adecl ) + } + + var req = 0; + val map = new mutable.HashMap[String,String](); + + // populate with special " + map.update( key, " + map.update( key, attValue ); + attribsTemplate = + add( attribsTemplate, Attribute(namespace, key, attValue)); + case dtd.IMPLIED => + } + } + + def add(as:List[Attribute], x:Attribute):List[Attribute] = { + if( x.namespace.length() == 0 ) + scala.xml.Attribute( namespace, x.key, x.value )::as; + else + x::as; + }; + + def validate( attributes:AttributeSeq ):AttributeSeq = { + var actualReq = 0; + var attribs: List[Attribute] = Nil; + for( val b <- attributes ) { + if( b.key == "xmlns" ) {} // this should not get even here + else if( b.namespace != namespace ) + attribs = add( attribs, b); + else attrSpec.get( b.key ) match { + case Some( AttrDecl( key, tpe, df )) => df match { + case DEFAULT( true, attValue ) => + if( b.value != attValue ) + ValidationException.fromFixedAttribute( key, attValue, b.value ); + else + add( attribs, b ) + case REQUIRED => + actualReq = actualReq + 1; + attribs = add( attribs, b ) + case _ => + attribs = add( attribs, b ) + } + case _ => + ValidationException.fromUndefinedAttribute( b.key ) + } + } + if( req - actualReq > 0 ) { + var allKeys = new mutable.HashSet[String](); + for( val AttrDecl( key, _, REQUIRED ) <- attrSpec.values ) + allKeys += key; + for( val a <- attribs ) + allKeys -= a.key; + ValidationException.fromMissingAttribute( allKeys ) + } + new AttributeSeq( (attribsTemplate:::attribs):_* ) + } +} diff --git a/sources/scala/xml/nobinding/NoBindingFactoryAdapter.scala b/sources/scala/xml/nobinding/NoBindingFactoryAdapter.scala index cc5077ab1a..03afa193ad 100644 --- a/sources/scala/xml/nobinding/NoBindingFactoryAdapter.scala +++ b/sources/scala/xml/nobinding/NoBindingFactoryAdapter.scala @@ -32,19 +32,19 @@ class NoBindingFactoryAdapter extends FactoryAdapter { */ def createNode(uri:String, label: String, - attrs: mutable.HashMap[String,String], + attrs: mutable.HashMap[Pair[String,String],String], children: List[Node] ):Elem = { val uri$ = uri.intern(); val elHashCode = Utility.hashCode( uri$, label, attrs, children ) ; - val attrMap = immutable.TreeMap.Empty[String,String] incl attrs; + val attrSeq = AttributeSeq.fromHashMap( attrs ); cache.get( elHashCode ).match{ case Some(cachedElem) => //System.err.println("[using cached elem +"+cachedElem.toXML+"!]"); //DEBUG cachedElem case None => - val el = Elem( uri$, label, attrMap, children:_* ); + val el = Elem( uri$, label, attrSeq, children:_* ); cache.update( elHashCode, el ); el } diff --git a/sources/scalac/transformer/matching/BerrySethi.java b/sources/scalac/transformer/matching/BerrySethi.java index a97552702a..9e9186e1f8 100644 --- a/sources/scalac/transformer/matching/BerrySethi.java +++ b/sources/scalac/transformer/matching/BerrySethi.java @@ -1,3 +1,5 @@ +/** $Id */ + package scalac.transformer.matching ; import scalac.Unit ; @@ -365,16 +367,19 @@ class BerrySethi { this.posMap.put( pat, i ); this.labelAt.put( i, label ); if( label != Label.DefaultLabel ) { + /* if( this.labels.contains( label ) ) { switch(label) { case TreeLabel(Apply(_, Tree[] args)): if( args.length > 0 ) { - unit.error(pat.pos, "sorry, this version of scalac cannot handle this pattern correctly"); + unit.warning(pat.pos, "if this pattern in nondeterminism, it will not compile correctly"); } } } + */ this.labels.add( label ); } + } /** overriden in BindingBerrySethi diff --git a/sources/scalac/transformer/matching/Label.java b/sources/scalac/transformer/matching/Label.java index f03321cb22..30a105dec4 100644 --- a/sources/scalac/transformer/matching/Label.java +++ b/sources/scalac/transformer/matching/Label.java @@ -31,7 +31,7 @@ public class Label { return lit.value.hashCode(); case TreeLabel( Tree pat ): // if pat is an Apply, than this case can only be correctly - // handled if it has no arguments...or there are no collisions + // handled there are no other similar Applys (nondeterminism) return pat.type().hashCode(); case TypeLabel( Type type ): return type.hashCode(); diff --git a/test/bin/scala-test b/test/bin/scala-test index c548b57438..1735b0b158 100755 --- a/test/bin/scala-test +++ b/test/bin/scala-test @@ -145,6 +145,7 @@ test_xml() { output="$OBJDIR"/`expr "$source" : "\(.*\)\\.scala"`-$KIND.obj; dtdfile="`expr "$source" : "\(.*\)\\.scala"`.dtd"; xmlfile="`expr "$source" : "\(.*\)\\.scala"`.xml"; + nsfile="`expr "$source" : "\(.*\)\\.scala"`.namespace"; objfile="$output/dtd.scala"; classpath="$output"; if $CYGWIN; then @@ -155,7 +156,7 @@ test_xml() { fi; rm -rf "$output"; mkdir -p "$output" && - $DTD2SCALA -d "$os_output" "$dtdfile" dtd && + $DTD2SCALA -d "$os_output" "$dtdfile" dtd `cat $nsfile` && $SOCOS -d "$os_output" $TEST_FLAGS $FLAGS "$objfile" "$source" && $SCALA -classpath "$classpath" Test "$xmlfile" && rm -rf "$output"; diff --git a/test/files/jvm/xmlLiterals.scala b/test/files/jvm/xmlLiterals.scala index d908a49349..65abd50094 100644 --- a/test/files/jvm/xmlLiterals.scala +++ b/test/files/jvm/xmlLiterals.scala @@ -11,7 +11,7 @@ import scala.collection.immutable ; object Test { - val e = immutable.TreeMap.Empty[String,String]; + val e = Node.NoAttributes; /* a helper function to compare up to whitespace */ @@ -77,9 +77,9 @@ object Test { Elem("","h1",e,Text("Hello World")), Elem("","p",e,Text("Check the "), Elem("","a", e,Text("scala")) - % Pair("href","scala.epfl.ch"), + % (Attribute("","href","scala.epfl.ch")), Text("page!")) - ) % Pair("background","#FFFFFF") + ) % Attribute("", "background","#FFFFFF") ).toString() )); diff --git a/test/files/jvm/xmlstuff.check b/test/files/jvm/xmlstuff.check index b348451a28..397a8970d6 100644 --- a/test/files/jvm/xmlstuff.check +++ b/test/files/jvm/xmlstuff.check @@ -38,3 +38,12 @@ List(Blabla) +41 21 693 68 67 +41 79 602 23 23 +patterns +passed ok +passed ok +passed ok +namespaces +passed ok +passed ok +passed ok +passed ok diff --git a/test/files/jvm/xmlstuff.scala b/test/files/jvm/xmlstuff.scala index 2617110372..f05548aa96 100644 --- a/test/files/jvm/xmlstuff.scala +++ b/test/files/jvm/xmlstuff.scala @@ -6,7 +6,7 @@ import scala.xml.{Node,NodeSeq,Elem,Text}; object Test with Application { - val e = scala.collection.immutable.TreeMap.Empty[String,String]; + val e = Node.NoAttributes; /* def eq( a:Seq[Node], b:Seq[Node] ):boolean = { @@ -30,7 +30,7 @@ object Test with Application { def label = "hello"; def namespace = ""; def child = List(Elem("","world",e)); - def attribute = e; + def attributes = e; }; assertSameElements( List( 3 ), List( 3 )); @@ -274,5 +274,53 @@ val addrBook = )); + /* patterns */ + Console.println("patterns"); + assertEquals( match { case => true; case _ => false; }, + true); + + assertEquals( + + + match { case + + => true; + case _ => false; }, + true); + + assertEquals( + + crazy text world + match { case + crazy text world + => true; + case _ => false; }, + true); + + /* namespaces */ + Console.println("namespaces"); + val cuckoo = + + + ; + assertEquals( cuckoo.namespace, "http://cuckoo.com"); + for( val n <- cuckoo.child ) { + assertEquals( n.namespace, "http://cuckoo.com"); + } + + /* + assertEquals( true, cuckoo match { + case + + + => true; + case _ => false; }); +*/ + assertEquals( false, cuckoo match { + case + + + => true; + case _ => false; }); } diff --git a/test/files/xml/lnk.check b/test/files/xml/lnk.check index f9f959fd5a..e7dca84cc8 100644 --- a/test/files/xml/lnk.check +++ b/test/files/xml/lnk.check @@ -1,5 +1,5 @@ -hello-link -LDAP LinksOpenLDAP Administration Guidecontains very readable section "What is LDAP"LDAP RFCsRFC2251 Lightweight Directory Access Protocol (v3)Lightweight Directory Access Protocol (v3): +hello-link +LDAP LinksOpenLDAP Administration Guidecontains very readable section "What is LDAP"LDAP RFCsRFC2251 Lightweight Directory Access Protocol (v3)Lightweight Directory Access Protocol (v3): Attribute Syntax DefinitionsLightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names The String Representation of LDAP Search FiltersLDAP and Lotus NotesLDAP enabled on Notes... to change settingsReplicating Notes data to OpenLDAP diff --git a/test/files/xml/lnk.namespace b/test/files/xml/lnk.namespace new file mode 100644 index 0000000000..5d787d9564 --- /dev/null +++ b/test/files/xml/lnk.namespace @@ -0,0 +1 @@ +http://scala.epfl.ch/testSuite/links \ No newline at end of file diff --git a/test/files/xml/lnk.scala b/test/files/xml/lnk.scala index 962c2f9149..8d81eae609 100644 --- a/test/files/xml/lnk.scala +++ b/test/files/xml/lnk.scala @@ -1,6 +1,6 @@ // $Id$ -import scala.xml.Node; +import scala.xml.{Attribute, AttributeSeq, Node}; import dtd._; object Test { @@ -14,7 +14,12 @@ object Test { // !!! System.out.println(b.toXML); // construct data using constructor - val c = Link(n + "target" -> "http://www.scala.org", Name(n, scala.xml.Text("hello-link"))); + val c = Link( + new AttributeSeq( + Attribute("","target","http://www.scala.org") + ), + Name(n, scala.xml.Text("hello-link")) + ); //c.getAttribs.update("target", "http://www.scala.org"); System.out.println( c ); diff --git a/test/files/xml/xhtml.check b/test/files/xml/xhtml.check index 50186a8428..1cb0cbf6b4 100644 --- a/test/files/xml/xhtml.check +++ b/test/files/xml/xhtml.check @@ -1,2 +1,2 @@ -a basic xhtml page

Welcome to xhtml in scala

a paragraph

another one, with a link to the LAMP homepage.

-a basic xhtml page

Welcome to xhtml in scala

a paragraph

another, with a example link to lamp homepage

+a basic xhtml page

Welcome to xhtml in scala

a paragraph

another one, with a link to the LAMP homepage.

+a basic xhtml page

Welcome to xhtml in scala

a paragraph

another, with a example link to lamp homepage

diff --git a/test/files/xml/xhtml.namespace b/test/files/xml/xhtml.namespace new file mode 100644 index 0000000000..4685ae94e0 --- /dev/null +++ b/test/files/xml/xhtml.namespace @@ -0,0 +1 @@ +http://www.w3.org/1999/xhtml \ No newline at end of file diff --git a/test/files/xml/xhtml.scala b/test/files/xml/xhtml.scala index e1510456c7..07cf3a1a74 100644 --- a/test/files/xml/xhtml.scala +++ b/test/files/xml/xhtml.scala @@ -1,6 +1,6 @@ // $Id$ -import scala.xml.{Node,Text}; +import scala.xml.{Attribute, AttributeSeq, Node, Text}; import dtd._; object Test { @@ -8,8 +8,12 @@ object Test { val n = Node.NoAttributes; def main( argv:Array[String] ) = { - val link = A( n + "href" -> "http://lampwww.epfl.ch", - Text("link")); + val link = A( + new AttributeSeq( + Attribute("","href","http://lampwww.epfl.ch") + ), + Text("link") + ); //val m = new scala.collection.mutable.HashMap[String, String]; //m.put("href","http://lampwww.epfl.ch"); @@ -21,7 +25,10 @@ object Test { P(n,Text("another one, with a "),link,Text(" to the LAMP homepage."))); val page = Html(n, Head(n, - Base(n+"href" -> "http://here.edu"), + Base( + new AttributeSeq( + Attribute("","href","http://here.edu") + )), Title(n,Text("a basic xhtml page"))), body); System.out.println( page ) ; -- cgit v1.2.3