From 4cf8078dabdf96dd47b512b928ef77ebac2d2a93 Mon Sep 17 00:00:00 2001 From: buraq Date: Mon, 17 Nov 2003 10:59:53 +0000 Subject: changes to XML API and parameterized binding in... changes to XML API and parameterized binding in dtd2scala better names changes to test suite --- sources/scala/tools/dtd2scala/DeclToScala.scala | 294 ++++++++---------------- sources/scala/tools/dtd2scala/XMLDecl.scala | 7 +- sources/scala/xml/BindingFactoryAdapter.scala | 71 ++++++ sources/scala/xml/FactoryAdapter.scala | 110 ++++----- sources/scala/xml/Generic.scala | 158 ------------- sources/scala/xml/Node.scala | 36 +++ sources/scala/xml/ScalaFactoryAdapter.scala | 78 ------- sources/scala/xml/Text.scala | 24 ++ sources/scala/xml/Utility.scala | 67 ++++++ sources/scala/xml/nobinding/Element.scala | 17 ++ sources/scala/xml/path/Expression.scala | 6 +- sources/scala/xml/path/Parser.scala | 9 +- 12 files changed, 369 insertions(+), 508 deletions(-) create mode 100644 sources/scala/xml/BindingFactoryAdapter.scala create mode 100644 sources/scala/xml/Node.scala delete mode 100644 sources/scala/xml/ScalaFactoryAdapter.scala create mode 100644 sources/scala/xml/Text.scala create mode 100644 sources/scala/xml/Utility.scala create mode 100644 sources/scala/xml/nobinding/Element.scala (limited to 'sources') diff --git a/sources/scala/tools/dtd2scala/DeclToScala.scala b/sources/scala/tools/dtd2scala/DeclToScala.scala index 1b1210dae1..f5c8191159 100644 --- a/sources/scala/tools/dtd2scala/DeclToScala.scala +++ b/sources/scala/tools/dtd2scala/DeclToScala.scala @@ -3,16 +3,94 @@ package scala.tools.dtd2scala ; import java.io.PrintWriter ; import scala.collection.Map ; +import scala.collection.mutable.HashMap ; import scalac.util.Name ; -import scalac.ast.parser.Scanner ; // for keywords +import scalac.ast.parser.Scanner ; /* for keywords */ + +import scala.xml._ ; +import scala.xml.nobinding.{Element,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, ElemDecl ] ) { - final val DEFAULT_moduleName = "myXML"; + abstract class objectTemplate { + val objectName : String = "myXML"; /* DEFAULT MODULE NAME */ + val package_ : String = ""; + val compress : boolean = true; + final val tmplFile = "scala/tools/dtd2scala/template/CommentTemplate.scala.xml"; + final val tmpl:Node = XML.load( ClassLoader.getSystemResource(tmplFile) ); + + val lookup : HashMap[String,String] = new HashMap[String,String](); + var curAttribs: Map[String,AttrDecl] = null ; /* of current elem */ + + def write:Unit = { + def writeNode( n:Node ):Unit = { + n.label match { + case "template" => { + lookup.update("objectName", objectName); + lookup.update("compressDefault", compress.toString()); + n.children.elements.foreach { n => writeNode(n) } + } + case "elementBinding" => { + for( val decl <- elemMap.values.elements ) do { + fOut.println(); + printIndent(); + lookup += "elementName" -> decl.name; + lookup += "elementContainsText" -> decl.containsText.toString(); + lookup += "elementContentModel" -> decl.contentModel; + curAttribs = decl.attribs; + n.children.elements.foreach{ n => writeNode( n ) } + } + curAttribs = null; + lookup -= "elementName"; + lookup -= "elementContainsText"; + } + + case "attributeAssign" => { + for( val aDecl <- curAttribs.keys.elements ) do { + lookup += "attributeName" -> aDecl; + n.children.elements.foreach{ n => writeNode( n ) } + } + lookup -= "attributeName"; + } + + case "attributeBinding" => { + for( val aDecl <- curAttribs.keys.elements ) do { + lookup += "attributeName" -> aDecl; + Console.println("attributeName is "+aDecl+" = "+lookup("attributeName")); + n.children.elements.foreach{ n => writeNode( n ) } + } + lookup -= "attributeName"; + } + case "ccstring" => { + Console.println("ccstring ref=\""+n.attributes("ref")+"\""); + fOut.print( cookedCap( lookup( n.attributes("ref") ) )); + } + case "string" => { + Console.println("string ref=\""+n.attributes("ref")+"\""); + fOut.print( lookup( n.attributes("ref") ) ); + } + case "qstring" => { + Console.println("qstring ref=\""+n.attributes("ref")+"\""); + + fOut.print("\""); + fOut.print( lookup( n.attributes("ref") ) ); + fOut.print("\""); + } + case "br" => { fOut.println(); printIndent() } + case "inc" => fIndent = fIndent + IND_STEP + case "dec" => fIndent = fIndent - IND_STEP + case "#PCDATA" => fOut.print( n.asInstanceOf[Text].text ) + case _ => error("what shall I do with a \""+n.label+"\" node ?") + } + } + writeNode( tmpl ) + } + } final val COMPRESS_DEFAULT:String = "true"; // do hash-consing on load @@ -23,15 +101,15 @@ class DeclToScala(fOut:PrintWriter, final val CHILDREN_VALDEF:String = "var _ch:scala.Seq[scala.xml.Element] = if( children == null ) { scala.Nil } else children ;"; - final val CHILDREN_SEQ:String = "children"; + final val CHILDREN_SEQ:String = "ch"; //static final String CHILDREN_T = "scala.Seq[Element]"; final val CHILDREN_T:String = "Element*"; - final val RAW_NAME_DEF:String = "def getName:String = "; + final val RAW_NAME_DEF:String = "def label:String = "; - final val GET_CHILDREN_DEF:String = "def getChildren:scala.Seq[scala.xml.Element] = _ch ;"; - final val GET_ATTRIBS_DEF:String = "def getAttribs:Map[String,String] = _at ;"; - final val SET_ATTRIBS_DEF:String = "def setAttribs( m:Map[String,String] ):Unit = {_at = m};"; + final val GET_CHILDREN_DEF:String = "def children:scala.Seq[scala.xml.Element] = ch ;"; + //final val GET_ATTRIBS_DEF:String = "def getAttribs:Map[String,String] = _at ;"; + //final val SET_ATTRIBS_DEF:String = "def setAttribs( m:Map[String,String] ):Unit = {_at = m};"; //static final String HASHCODE_DEF = "override def hashCode():int = { getChildren.hashCode() + getAttribs.hashCode() + getName.hashCode() }"; @@ -39,33 +117,8 @@ class DeclToScala(fOut:PrintWriter, var fIndent:int = 0; - def begin = { - fOut.println( "// this file is generated from a DTD"); - fOut.print( "object " ); - fOut.print( moduleName ); - fOut.println(" {"); - fIndent = IND_STEP; - printIndent(); - fOut.println("import scala.xml._ ;"); - fOut.println("import scala.collection.Map ;"); - fOut.println("import scala.collection.mutable.HashMap ;"); - } - - def end = { - fOut.println("}"); - fOut.flush(); - fOut.close(); - } + /* - /** - * @name - (raw) name of the Element - */ - def printClassDef( decl:ElemDecl ):Unit = { - val clazzName:String = cookedCap( decl.name ); - - printIndent(); - - /* // DISABLED // convenience ! overloaded constructors, have to appear *before* // the class def and need the "final" modifier @@ -84,44 +137,9 @@ class DeclToScala(fOut:PrintWriter, printIndent(); } - */ - - - fOut.print( "case class " ); - - fOut.print( clazzName ); - - fOut.print('('); - //fOut.print( ATTRIB_MAP ); fOut.print(':'); fOut.print( ATTRIB_T ); - //fOut.print(','); - fOut.print( CHILDREN_SEQ ); fOut.print(':'); fOut.print( CHILDREN_T ); - fOut.print(')'); - fOut.println(" extends Element {"); - fIndent = fIndent + IND_STEP ; - - printIndent(); - fOut.print( RAW_NAME_DEF ); - fOut.print('"'); fOut.print( decl.name ); fOut.print('"'); - fOut.println(';'); - - printIndent(); fOut.println( CHILDREN_VALDEF ); - printIndent(); fOut.println( GET_CHILDREN_DEF ); - printIndent(); fOut.println( ATTRIBS_VARDEF ); - printIndent(); fOut.println( GET_ATTRIBS_DEF ); - printIndent(); fOut.println( SET_ATTRIBS_DEF ); - //printIndent(); fOut.println( HASHCODE_DEF ); - - /* - for( Iterator it = decl.attribs.keySet().iterator(); it.hasNext() ; ) - toScala( (AttrDecl) decl.attribs.get( it.next() )); */ - fIndent = fIndent - IND_STEP ; - printIndent(); - fOut.println("}"); - fOut.flush(); - } /** Prints the indent. */ def printIndent():Unit = { @@ -130,141 +148,15 @@ class DeclToScala(fOut:PrintWriter, } } - /** - * @name - (raw) name of the attribute - */ - def printSetMethod( name:String ):Unit = { - printIndent(); - - fOut.print( "def " ); - fOut.print( cooked( name )); - fOut.print( "( arg:String ):Unit = _at.update(\"" ); - fOut.print( name ); - fOut.println( "\", arg ) ;"); - } - - /** - * @name - (raw) name of the attribute - */ - def printGetMethod( name:String ):Unit = { - printIndent(); - - fOut.print( "def " ); - fOut.print( cooked( name )); - fOut.print( ":String = _at.get( "); - fOut.print( '"' );fOut.print( name );fOut.print( '"' ); - fOut.println( " ).match { Some(x) => x };"); - } - - def printFactory:Unit = { - - printIndent(); - fOut.println( - "val _factory: Map[String, scala.Seq[scala.xml.Element] => scala.xml.Element] = {"); - fIndent = fIndent + IND_STEP; - printIndent(); - fOut.println( - //"val res = new scala.HashMap[String,(scala.Map[String,String],scala.Seq[Element])=>Element] ;"); - "val res = new HashMap[String, scala.Seq[scala.xml.Element] => scala.xml.Element] ;"); - //JAVA: for(Iterator it = elemMap.keySet().iterator(); it.hasNext(); ) - for( val decl <- elemMap.values.elements ) do { - printIndent(); - fOut.print( "res.update(\"" ); - fOut.print( decl.name ); - fOut.print( "\",(x:scala.Seq[scala.xml.Element] => "); - fOut.print( cookedCap( decl.name )); - fOut.println("( x:_* ) ));"); - }; - printIndent(); - fOut.println("res"); - printIndent(); - fOut.println( "}"); - fIndent = fIndent - IND_STEP; - - } - - def printContainsTextDef:Unit = { - printIndent(); - fOut.println("val _containsMap: Map[scala.String, boolean] = {"); - fIndent = fIndent + IND_STEP; - printIndent(); - fOut.println("val res = new HashMap[scala.String, boolean] ;"); - - for( val decl <- elemMap.values.elements ) do { - printIndent(); - fOut.print( "res.update(\"" ); - fOut.print( decl.name ); - fOut.print( "\","); - fOut.print( decl.containsText ); //write a boolean literal - fOut.println(");"); - }; - printIndent(); - fOut.println("res"); - fIndent = fIndent - IND_STEP; - printIndent(); - fOut.println( "}"); - } - - def printLoaderDef:Unit = { - printIndent(); - fOut.print("def load( filename:String ):Element = load( filename, "); - fOut.print( COMPRESS_DEFAULT ); - fOut.println(");"); - printIndent(); - fOut.println("def load( filename:String, _compress:boolean ):Element = {"); - fIndent = fIndent + IND_STEP; - printIndent(); - fOut.println("val fAdapter = new ScalaFactoryAdapter {"); - fIndent = fIndent + IND_STEP; - printIndent(); - fOut.println("val f = _factory ;"); - printIndent(); - fOut.println("val g = _containsMap ; "); - printIndent(); - fOut.println("val compress = _compress ; "); - printIndent(); - fOut.println("};"); - fIndent = fIndent - IND_STEP; - printIndent(); - fOut.println("fAdapter.loadXML( filename );"); - printIndent(); - fOut.println("};"); - fIndent = fIndent - IND_STEP; - }; - -def comment = { - fOut.println("/** the following elements are there"); - - for( val decl <- elemMap.values.elements ) do { - fOut.print(" * "); - fOut.print( decl.name ); - fOut.print(" : ["); - fOut.print( decl.contentModel ); - fOut.println(']'); - fOut.print(" * "); - fOut.println( "attribs: "+decl.attribs ); - }; - fOut.println("*/"); -} - + /** runs translation. */ def run:Unit = { - begin; - comment; - for( val decl <- elemMap.values.elements ) do toScala( decl ); - printContainsTextDef; - printFactory; - printLoaderDef; - end; + new objectTemplate { + override val objectName = moduleName + }.write; + fOut.flush(); + fOut.close(); } - def toScala( decl:XMLDecl ):Unit = decl.match { - - case x:ElemDecl => printClassDef( x ); - case AttrDecl( name, "CDATA" ) => printGetMethod(name); - case AttrDecl( name, tpe ) => warning_attrib( name, tpe ) - case _ => error("unexpected decl:"+decl); - } // toScala - private def warning_attrib(name:String,tpe:String) = { System.err.print( "[ignoring attribute \"" ); System.err.print( name ); @@ -277,7 +169,7 @@ def comment = { // // cooking raw names // - +/* replace dash, colons with underscore, keywords with appended $ */ private def cooked( ckd:StringBuffer, raw:String, off:int ):String = { for( val i <- List.range( off, raw.length()) ) do { val _ = raw.charAt( i ).match { diff --git a/sources/scala/tools/dtd2scala/XMLDecl.scala b/sources/scala/tools/dtd2scala/XMLDecl.scala index 50edccf8a0..4e17124a39 100644 --- a/sources/scala/tools/dtd2scala/XMLDecl.scala +++ b/sources/scala/tools/dtd2scala/XMLDecl.scala @@ -10,8 +10,7 @@ case class ElemDecl( name:String , extends XMLDecl { def containsText = contentModel.indexOf("#PCDATA") != -1 ; -}; /*AttrDecl[]*/ +}; -// ignore default values 4 now -case class AttrDecl( name:String, - tpe:String ) extends XMLDecl; +/* ignore default values 4 now */ +case class AttrDecl( name:String, tpe:String ) extends XMLDecl; diff --git a/sources/scala/xml/BindingFactoryAdapter.scala b/sources/scala/xml/BindingFactoryAdapter.scala new file mode 100644 index 0000000000..ea3a1ca19a --- /dev/null +++ b/sources/scala/xml/BindingFactoryAdapter.scala @@ -0,0 +1,71 @@ +package scala.xml ; + +import scala.collection.Map ; +import scala.collection.mutable.HashMap ; +//import scala.xml.javaAdapter.Map ; +//import scala.xml.javaAdapter.HashMap ; + +/** a Scala specific dtd2scala.FactoryAdapter, which plays the SAX content +* handler for the SAX parser. It implements the three callback methods +* elementContainsText, createNode and createPCDATA. DTDs imported with +* the dtd2scala tool all use this class as interface to the SAX XML parser, +* by giving concrete values for the factory maps f and g. +*/ + +abstract class BindingFactoryAdapter extends FactoryAdapter() { + + /** mapping from element names to an element constructor + * (constr:Seq[Node],HashMap[String,String] => Node) + */ + val f: Map[ String, (Seq[Node],HashMap[String,String]) => Node ]; + + /** mapping from element names to a truth value indicating + * whether the corresponding element may have text children + */ + val g: Map[ String, boolean ] ; + + /** if true, create only elements not created before */ + val compress: boolean ; + + /** looks up whether an element may have text children */ + def nodeContainsText( name:java.lang.String ):boolean = + g.get( name ).match { case Some(x) => x }; + + // if compress is set, used for hash-consing + val cache = new HashMap[int,Node]; + //var cacheCount = 0; + + /** creates an element. see also compress */ + def createNode(elemName:String, + attribs:HashMap[String,String], + children:List[Node] ):Node = { + // 2do:optimize + if( !compress ) { + // get constructor + val c = f.get( elemName ).match{ case Some(x) => x }; + c( children, attribs ); + } else { // do hash-consing + + val ahc = attribs.toList.hashCode(); + val h = Utility.hashCode( elemName, ahc, children ); + cache.get( h ).match { + + case Some(cachedElem) => + //cacheCount = cacheCount + 1; + //System.err.println("[ScalaFactoryAdapter: cache hit "+cacheCount+"]"); + cachedElem + + case None => + // get constructor + val c = f.get( elemName ).match{ case Some(x) => x }; + val el = c( children, attribs ); + cache.update( h, el ); + el + } + } + } // createNode + + /** creates PCDATA element */ + def createText( text:String ):Text = new Text( text ); + +} // BindingFactoryAdapter diff --git a/sources/scala/xml/FactoryAdapter.scala b/sources/scala/xml/FactoryAdapter.scala index dbe332283b..2d6e5464c5 100644 --- a/sources/scala/xml/FactoryAdapter.scala +++ b/sources/scala/xml/FactoryAdapter.scala @@ -1,11 +1,10 @@ package scala.xml ; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; +import java.io.{OutputStream,OutputStreamWriter,PrintWriter,Writer}; import java.io.UnsupportedEncodingException; -import java.io.Writer; +import java.net.URL; + /*import java.util.Map; import java.util.HashMap; @@ -13,8 +12,7 @@ import java.util.LinkedList; import java.util.Stack; import java.util.Iterator; */ -import scala.collection.mutable.Stack; -import scala.collection.mutable.HashMap; +import scala.collection.mutable.{HashMap,Stack}; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; @@ -53,7 +51,7 @@ abstract class FactoryAdapter val buffer = new StringBuffer(); val attribStack = new Stack[HashMap[String,String]]; - val hStack = new Stack[Element]; // [ element ] contains siblings + val hStack = new Stack[Node]; // [ element ] contains siblings val tagStack = new Stack[String]; // [String] var curTag : String = null ; @@ -64,23 +62,23 @@ abstract class FactoryAdapter /** Tests if an XML element contains text. * @return true if element named localName contains text. */ - def elementContainsText( localName:String ):boolean ; // abstract + def nodeContainsText( localName:String ):boolean ; // abstract - /** Creates an new XML element. + /** creates an new non-text(tree) node. * @param elemName * @param attribs * @param chIter * @return a new XML element. */ - def createElement(elemName:String , - attribs:HashMap[String,String] , - chIter:List[Element] ):Element; //abstract + def createNode(elemName:String , + attribs:HashMap[String,String] , + chIter:List[Node] ):Node; //abstract - /** Creates an PCDATA element. + /** creates a Text node. * @param text - * @return a new PCDATA element. + * @return a new Text node. */ - def createPCDATA( text:String ):PCDATA; // abstract + def createText( text:String ):Text; // abstract // // ContentHandler methods @@ -88,26 +86,22 @@ abstract class FactoryAdapter /** Set document locator. * @param locator + def setDocumentLocator( locator:Locator ):Unit = {} */ - //def setDocumentLocator( locator:Locator ):Unit = {} /** Start document. * @throws org.xml.sax.SAXException if .. + def startDocument():Unit = {} */ - //def startDocument():Unit /*throws SAXException*/ = {} /** Characters. * @param ch * @param offset * @param length */ - override def characters(ch:Array[char] , offset:int , length:int ):Unit - /*throws SAXException*/ = { + override def characters( ch:Array[char] , offset:int , length:int ):Unit = { var ws:boolean = false; - - //System.err.println( "before \""+buffer+"\"" ); - if (capture) { var i:int = offset; while (i < offset + length) { @@ -126,38 +120,36 @@ abstract class FactoryAdapter //System.err.println( "after \""+buffer+"\"" ); //System.err.println(); - } // characters(char[],int,int) + } /** Ignorable whitespace. + def ignorableWhitespace(ch:Array[char] , offset:int , length:int ):Unit = {} */ - //def ignorableWhitespace(ch:Array[char] , offset:int , length:int ):Unit - // /*throws SAXException*/ = {} /** End document. */ - //def endDocument():Unit /*throws SAXException*/ = {} + //def endDocument():Unit = {} //var elemCount = 0; //STATISTICS - // - // ContentHandler methods - // - /** Start prefix mapping. - */ - //def startPrefixMapping( prefix:String , uri:String ):Unit - // /*throws SAXException*/ = {} + /* ContentHandler methods */ - /** Start element. + /* Start prefix mapping - use default impl. + def startPrefixMapping( prefix:String , uri:String ):Unit = {} */ - override def startElement( uri:String , localName:String , qname:String , - attributes:Attributes ):Unit /*throws SAXException*/ = { - //elemCount = elemCount + 1; //STATISTICS + + /* Start element. */ + override def startElement(uri:String, + localName:String, + qname:String , + attributes:Attributes ):Unit = { + /*elemCount = elemCount + 1; STATISTICS */ captureText(); tagStack.push(curTag); curTag = localName ; - capture = elementContainsText(localName) ; + capture = nodeContainsText(localName) ; hStack.push( null ); var map:HashMap[String,String] = null:HashMap[String,String]; @@ -192,7 +184,7 @@ abstract class FactoryAdapter if (capture == true) { val text = buffer.toString(); if(( text.length() > 0 )&&( !( text.equals(" ")))) { - val _ = hStack.push( createPCDATA( text ) ); + val _ = hStack.push( createText( text ) ); } } buffer.setLength(0); @@ -212,65 +204,59 @@ abstract class FactoryAdapter val attribMap = attribStack.top; attribStack.pop; // reverse order to get it right - var v:List[Element] = Nil; - var child:Element = hStack.top; hStack.pop; + var v:List[Node] = Nil; + var child:Node = hStack.top; hStack.pop; while( child != null ) { v = child::v; child = hStack.top; hStack.pop; } // create element - rootElem = createElement( localName, attribMap, v ); + rootElem = createNode( localName, attribMap, v ); hStack.push(rootElem); // set curTag = tagStack.top; tagStack.pop; if (curTag != null) // root level - capture = elementContainsText(curTag); + capture = nodeContainsText(curTag); else capture = false; } // endElement(String,String,String) /** End prefix mapping. + def endPrefixMapping(prefix:String ):Unit = {} */ - //def endPrefixMapping(prefix:String ):Unit /*throws SAXException*/ = {} /** Skipped entity. + def skippedEntity(name:String ):Unit /*throws SAXException*/ = {} */ - //def skippedEntity(name:String ):Unit /*throws SAXException*/ = {} // // ErrorHandler methods // /** Warning.*/ - override def warning(ex:SAXParseException ):Unit - /*throws SAXException*/ = { + override def warning(ex:SAXParseException ):Unit = { // ignore warning, crimson warns even for entity resolution! //printError("Warning", ex); } /** Error. */ - override def error(ex:SAXParseException ):Unit - /*throws SAXException*/ = { + override def error(ex:SAXParseException ):Unit = { printError("Error", ex); } - /** Fatal error. - */ - override def fatalError(ex:SAXParseException ):Unit - /*throws SAXException*/ = { + /** Fatal error.*/ + override def fatalError(ex:SAXParseException ):Unit = { printError("Fatal Error", ex); - /*throw ex;*/ } // // Protected methods // - /** Prints the error message. - */ + /** Prints the error message */ protected def printError( errtype:String , ex:SAXParseException ):Unit = { System.err.print("["); @@ -294,19 +280,21 @@ abstract class FactoryAdapter System.err.println(); System.err.flush(); - } // printError(String,SAXParseException) - //} // class FA_ErrorHandler - var rootElem : Element = null:Element; + } + + var rootElem : Node = null:Node; //FactoryAdapter // MAIN // + def loadXML( url:URL ):Node = loadXML( url.getFile() ); + /** load XML document * @param fileName * @return a new XML document object */ - def loadXML( fileName:String ):Element = { + def loadXML( fileName:String ):Node = { // variables //PrintWriter out = new PrintWriter(System.out); @@ -364,7 +352,7 @@ abstract class FactoryAdapter } } // catch //System.err.println("[FactoryAdapter: total #elements = "+elemCount+"]"); - rootElem; + rootElem } // loadXML diff --git a/sources/scala/xml/Generic.scala b/sources/scala/xml/Generic.scala index 6cd16c6742..f4a85f7ce5 100644 --- a/sources/scala/xml/Generic.scala +++ b/sources/scala/xml/Generic.scala @@ -1,159 +1 @@ package scala.xml; - -import scala.collection.Map ; -import scala.collection.mutable.HashMap ; - -/** Generic.load( ) will load the xml document from file and - * create a tree with scala.Labelled, PCDATA and scala.Symbol objects. - * Text can appear within PCDATA at the leaves. - */ - -object Generic { - - // utility functions - - /** TEMPORARY converting Java iterators to scala List - - def iterToList[ a ]( iter:java.util.Iterator ):List[a] = - if( !iter.hasNext() ) - Nil - else - (iter.next().asInstanceOf[ a ])::iterToList( iter ) ; - - */ - - /** TEMPORARY converting Java maps to javaAdapter maps - - def mapToMap[a,b]( map:java.util.Map ):Map[a,b] = { - - val keys:java.util.Iterator = map.keySet().iterator(); - val res = new HashMap[a,b] ; - - def iterToMap:Unit = - if( keys.hasNext() ) { - val key = keys.next(); - val value = map.get( key ).asInstanceOf[ b ]; - res.put( key.asInstanceOf[ a ], value.asInstanceOf[ b ]); - iterToMap - } else - () ; - - iterToMap; - res - } - */ - - /** turns a Map that contains attributes into XML like att1="val1" att2="val2" - */ - - def toXML( attrib:Map[ String, String ] ):String = { - def iterate( keys:Iterator[String] ) = - if( keys.hasNext ) { - val key = keys.next; - " " + key + "=\"" + attrib.get( key ).match{ case Some(x) => x } + "\""; - } else { - "" - } - - if( attrib != null ) iterate( attrib.keys.elements ) else ""; - } - - // attributes - - /** this trait is mixed in with Labelled in order to provide attributes - */ - - trait Attribbed { - - // only CDATA / String attributes for now - def attribs : Map[String,String] ; - - } - // functions for generic xml loading, saving - - /** will load the given file and return a generic XML representation - */ - - def load( filename:String ):Labelled = { - val b = new GenericFactoryAdapter().loadXML( filename ); - b.asInstanceOf[Labelled] - }; - - /** will save a generic XML representation doc to filename. Uses NIO classes of JDK 1.4 for character conversion, - * encoding is fixed to ISO-8859-1 for now. - */ - - def save( filename:String, doc:Any ):Unit = { - import java.io.{FileOutputStream,Writer}; - import java.nio.channels.{Channels,FileChannel}; - def toXMLList( xs: List[Any], fc:Writer ):Unit = xs match { - case _::ys => - toXML( xs.head, fc ); - toXMLList( ys, fc ); - case _ => () - } - def toXML( doc: Any, fc:Writer ):Unit = doc match { - case PCDATA( s ) => - fc.write( (doc.asInstanceOf[ PCDATA ]).toXML ); - case Labelled( Symbol( tag ), xs ) => - fc.write( "<" ); - fc.write( tag ); - fc.write( Generic.toXML(( doc.asInstanceOf[ Attribbed ]) - .attribs )); - fc.write( ">" ); - toXMLList( xs, fc ); - fc.write( "" ); - - } - val fos = new FileOutputStream( filename ); - val w = Channels.newWriter( fos.getChannel(), "ISO-8859-1" ); - toXML( doc, w ); - w.close(); - fos.close(); - } - - /** this class contains methods called back by the parser to create elements. - * It implements hash-consing, i.e. identical elemens (same tag, same attributes, same children) - * are only constructed once ! - */ - - class GenericFactoryAdapter extends FactoryAdapter() { - - def elementContainsText( name:java.lang.String ):boolean = true; - - // default behaviour is hash-consing - val cache = new HashMap[Element,Element]; - - def createElement( elemName: String, - attrs: HashMap[String,String], - children: List[Element] ):Element = { - - val el = new Labelled( Symbol( elemName ), children ) - with Attribbed with Element { - def getName = elemName; - def getChildren = children; - def attribs = attrs ; // ?! not needed anymore - def getAttribs = attrs; - def setAttribs( m:Map[ String, String ] ) = { - /* FIXME throw error */ }; - }; - - cache.get( el ).match{ - case Some(cachedElem) => - System.err.println("[using cached elem!]"); - cachedElem - case None => - cache.update( el, el ); - el - } - }; // createElement - - def createPCDATA( text:String ):PCDATA = { - new PCDATA( text ); - }; - - } // GenericFactoryAdapter - -} //Generic diff --git a/sources/scala/xml/Node.scala b/sources/scala/xml/Node.scala new file mode 100644 index 0000000000..0ae73a7618 --- /dev/null +++ b/sources/scala/xml/Node.scala @@ -0,0 +1,36 @@ +package scala.xml ; + +import scala.collection.Map; + +/** superclass for specific representation of XML elements. These are created by +** a xxx2scala binding tool +**/ +abstract class Node { + + def label: String; + def children: Seq[ Node ]; + + /** returns a mapping from all present attributes to values */ + def attributes: Map[String,String]; + + protected val attribHashCode: int; + + /** hashcode for this node*/ + override def hashCode() = Utility.hashCode( label, attribHashCode, children ); + + def toXML: String = Utility.toXML( this ); + + def toXML_( elems:Seq[Node] ):String = elems match { + case head :: tail => head.toXML + toXML_( tail ); + case Nil => ""; + } + + override def toString() = { + var s = new StringBuffer( label ); + s.append("("); + s.append( children.toString() ); + s.append(")"); + s.toString(); + } + +} diff --git a/sources/scala/xml/ScalaFactoryAdapter.scala b/sources/scala/xml/ScalaFactoryAdapter.scala deleted file mode 100644 index 0d77ee7370..0000000000 --- a/sources/scala/xml/ScalaFactoryAdapter.scala +++ /dev/null @@ -1,78 +0,0 @@ -package scala.xml ; - -import scala.collection.Map ; -import scala.collection.mutable.HashMap ; -//import scala.xml.javaAdapter.Map ; -//import scala.xml.javaAdapter.HashMap ; - -/** a Scala specific dtd2scala.FactoryAdapter, which plays the SAX content -* handler for the SAX parser. It implements the three callback methods -* elementContainsText, createElement and createPCDATA. DTDs imported with -* the dtd2scala tool all use this class as interface to the SAX XML parser, -* by giving concrete values for the factory maps f and g. -*/ - -abstract class ScalaFactoryAdapter - extends FactoryAdapter() { - - /** subclasses need to provide a mapping from a element name (string) to an element constructor - * (constr:Seq[Element] => Element) - */ - val f: Map[ String, Seq[Element] => Element ]; - - /** subclasses need to provide a mapping from an element name (string) to a truth value indicating - * whether text (PCDATA) may appear as - */ - - val g: Map[ String, boolean ] ; - - /** subclasses need to tell whether they want to use hash-consing, which creates only non-identical elements - */ - val compress: boolean ; - - /** looks up in g whether an element may contain text (PCDATA) - */ - - def elementContainsText( name:java.lang.String ):boolean = - g.get( name ).match { case Some(x) => x }; - - // if compress is set, used for hash-consing - val cache = new HashMap[int,Element]; - //var cacheCount = 0; - /** creates an element. uses hash-consing if compress == true - */ - def createElement(elemName:String, - attribs:HashMap[String,String], - children:List[Element] ):Element = { - // 2do:optimize - if( !compress ) { - // get constructor - val c = f.get( elemName ).match{ case Some(x) => x }; - val el = c( children ); - el.setAttribs( attribs ); - el - } else { // do hash-consing - - val h = Element.hashValue( elemName, attribs, children ); - cache.get( h ).match { - - case Some(cachedElem) => - //cacheCount = cacheCount + 1; - //System.err.println("[ScalaFactoryAdapter: cache hit "+cacheCount+"]"); - cachedElem - - case None => - // get constructor - val c = f.get( elemName ).match{ case Some(x) => x }; - val el = c( children ); - el.setAttribs( attribs ); - cache.update( h, el ); - el - } - } - } // createElement - - /** creates PCDATA element */ - def createPCDATA( text:String ):PCDATA = new PCDATA( text ); - -} // ScalaFactoryAdapter diff --git a/sources/scala/xml/Text.scala b/sources/scala/xml/Text.scala new file mode 100644 index 0000000000..1070ded922 --- /dev/null +++ b/sources/scala/xml/Text.scala @@ -0,0 +1,24 @@ +/* BE */ +package scala.xml; + +import scala.collection.Map ; + +/** a text node. Used in both unsafe and typed XML representation +**/ + +case class Text( text:String ) extends Node { + + def label = "#PCDATA"; + + def children = Nil; + def attributes = null; + + protected val attribHashCode = 0; + + override def toXML = Utility.escape( text ); + + override def hashCode() = text.hashCode(); /* could be done at parse time */ + override def toString() = "Text("+text+")"; + +} // Text + diff --git a/sources/scala/xml/Utility.scala b/sources/scala/xml/Utility.scala new file mode 100644 index 0000000000..6e65dd104d --- /dev/null +++ b/sources/scala/xml/Utility.scala @@ -0,0 +1,67 @@ +package scala.xml ; + +import java.lang.StringBuffer ; /* Java dependency! */ +import scala.collection.Map ; +import scala.xml.nobinding.Element ; +/** Utility functions for processing instances of bound and not bound XML classes. +**/ + +object Utility { + + /** representation of text in a string that is well-formed XML. + ** the characters < > & and " are escaped + */ + def escape( text:String ) = { + val s = new StringBuffer(); + for( val c <- new IterableString(text).elements ) do c match { + case '<' => val _ = s.append("<"); + case '>' => val _ = s.append(">"); + case '&' => val _ = s.append("&"); + case '"' => val _ = s.append("""); + case _ => val _ = s.append( c ); + } + s.toString(); + } + + /** serializes an instance of Node to a string that contains well-formed XML **/ + def toXML( n:Node ):String = n match { + case Text( t ) => escape( t ); + case _ => + val s = new StringBuffer(); + s.append("<"); + s.append( n.label ); + if( null != n.attributes ) { + s.append( attr2xml( n.attributes.elements ) );{} + } + s.append(">"); + s.append( toXML( n.children.elements ) ); + s.append(""); + s.toString() + } + + def toXML( ch:Iterator[Node] ):String = { + ch.foldLeft ("") { (s:String,n:Node) => { val t:String = toXML( n ); s + t }} + } + + /** for a Node n, returns string representation of n.attributes **/ + def attr2xml( attrib:Iterator[Pair[String, String]] ):String = { + attrib.foldLeft ("") { (s:String,x:Pair[String,String]) => { + val t = new StringBuffer(" "); + t.append( s ); + t.append( x._1 ); + t.append("=\""); + t.append( x._2 ); + t.append("\""); + t.toString() + } + } + } + + /** returns a hashcode for the given constituents of a node */ + def hashCode( label:String, attribHashCode:int, children:Seq[Node] ) = { + label.hashCode() + attribHashCode + children.hashCode() + } + +} diff --git a/sources/scala/xml/nobinding/Element.scala b/sources/scala/xml/nobinding/Element.scala new file mode 100644 index 0000000000..5213ee8944 --- /dev/null +++ b/sources/scala/xml/nobinding/Element.scala @@ -0,0 +1,17 @@ +package scala.xml.nobinding; + +import scala.collection.Map ; +import scala.xml.Node ; + +/** an XML node. use this when data binding is not desired. +**/ +case class Element( symbol: Symbol, ch: List[Node] ) extends Node { + + def label = symbol.name; + def children = ch; + + protected val attribHashCode = 0; /* overriden at parse time */ + def attributes : Map[String,String] = null ; /* overriden at parse time */ + +} + diff --git a/sources/scala/xml/path/Expression.scala b/sources/scala/xml/path/Expression.scala index d9f81d5836..0289582ffe 100644 --- a/sources/scala/xml/path/Expression.scala +++ b/sources/scala/xml/path/Expression.scala @@ -1,9 +1,9 @@ package scala.xml.path; -import scala.xml.Element ; +import scala.xml.Node ; object Expression { - def evaluate(expr:Expression):List[Element] = { + def evaluate(expr:Expression):List[Node] = { Nil; // to do } } @@ -19,7 +19,7 @@ case class Node[T<:Element]( conds:Condition* ) extends Expression { } }; */ -case class Node( label:String, cond:Option[List[List[Expression]]] ) extends Expression ; +case class PathNode( label:String, cond:Option[List[List[Expression]]] ) extends Expression ; case class Attribute( name:String ) extends Expression; diff --git a/sources/scala/xml/path/Parser.scala b/sources/scala/xml/path/Parser.scala index e7828fe716..b675e4396f 100644 --- a/sources/scala/xml/path/Parser.scala +++ b/sources/scala/xml/path/Parser.scala @@ -22,9 +22,12 @@ class Parser( it:Iterator[char] ) with Scanner( it ) { case DOT => nextToken; acc( SLASH ); - case IDENT => - val label = value; nextToken; val cs = conds; es = Node( label, cs ) :: es ; - + case IDENT => { + val label = value; + nextToken; + val cs = conds; + es = PathNode( label, cs ) :: es + } case SLASH => nextToken; -- cgit v1.2.3