/*
 * (C) Copyright IBM Corp. 1997-1998  All rights reserved.
 *
 * US Government Users Restricted Rights Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *
 * The program is provided "as is" without any warranty express or
 * implied, including the warranty of non-infringement and the implied
 * warranties of merchantibility and fitness for a particular purpose.
 * IBM will not be liable for any damages suffered by you as a result
 * of using the Program. In no event will IBM be liable for any
 * special, indirect or consequential damages or lost profits even if
 * IBM has been advised of the possibility of their occurrence. IBM
 * will not be liable for any third party claims against you.
 */

package com.ibm.xml.parser;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.Vector;
import org.w3c.dom.*;

/**
 * An implementation of <code>ElementFactory</code>.
 *
 * @version 1.1
 * @author TAMURA Kent &lt;kent@trl.ibm.co.jp&gt;
 */
public class DefaultElementFactory implements ElementFactory, java.io.Serializable {
    /**
     * Make new <code>DefaultElementFactory</code> instance.
     */
    public DefaultElementFactory() {
    }

    static DefaultElementFactory s_instance = null;
    /**
     * Return <CODE>DefaultElementFactory</CODE> instance.
     */
    public static DefaultElementFactory getInstance() {
        if (null == s_instance)  s_instance = new DefaultElementFactory();
        return s_instance;
    }

    /**
     * Create <code>TXElement</code> instance.
     */
    public TXElement createElement(String name) {
        TXElement el = new TXElement(name);
        el.setFactory(this);
        return el;
    }

    /**
     * Create <code>TXAttribute</code> instance.
     */
    public TXAttribute createAttribute(String name, String value) {
        TXAttribute a = new TXAttribute(name, value);
        a.setFactory(this);
        return a;
    }

    /**
     * Create <code>TXText</code> instance.
     */
    public TXText createText(String data) {     ////
        return createText(data, false);
    }

    /**
     * Create <code>TXText</code> instance.
     */
    public TXText createText(String data, boolean ignorable) {
        TXText te = new TXText(data);
        te.setFactory(this);
        te.setIsIgnorableWhitespace(ignorable);
        return te;
    }

    /**
     * Create <code>TXText</code> instance.
     */
    public TXText createText(char[] chararray, int offset, int length, boolean isIgnorableWhitespace) {
        TXText te = new TXText(new String(chararray, offset, length));
        te.setFactory(this);
        te.setIsIgnorableWhitespace(isIgnorableWhitespace);
        return te;
    }

    /**
     * Create <code>TXCDATASection</code> instance.
     */
    public TXCDATASection createCDATA(String data) {
        TXCDATASection cdata = new TXCDATASection(data);
        cdata.setFactory(this);
        return cdata;
    }

    /**
     * Create <code>TXComment</code> instance.
     */
    public TXComment createComment(String data) { ////
        TXComment comm = new TXComment(data);
        comm.setFactory(this);
        return comm;
    }

    /**
     * Create <code>TXPI</code> instance.
     */
    public TXPI createPI(String name, String data) { //// 
        TXPI pi = new TXPI(name, data);
        pi.setFactory(this);
        return pi;
    }

    /**
     * Create <code>NamespacePI</code> instance.
     *
     * @see com.ibm.xml.parser.NamespacePI#NamespacePI(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
     */
    public NamespacePI createNamespacePI(String name, String data, String ns,
                                         String prefix, String src) {
        NamespacePI pi = new NamespacePI(name, data, ns, prefix, src);
        pi.setFactory(this);
        return pi;
    }

    /**
     * Create <code>StylesheetPI</code> instance.
     *
     * @see com.ibm.xml.parser.StylesheetPI#StylesheetPI(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
     */
    public StylesheetPI createStylesheetPI(String name, String data,
                                           String type, String href, String title) {
        StylesheetPI pi = new StylesheetPI(name, data, type, href, title);
        pi.setFactory(this);
        return pi;
    }

    /**
     * Create <code>TXDocument</code> instance.
     */
    public TXDocument createDocument() {
        TXDocument doc = new TXDocument();
        doc.setFactory(this);
        return doc;
    }

    /**
     * Create <code>DTD</code> instance.
     */
    public DTD createDTD() {
        DTD d = new DTD();
        d.setFactory(this);
        return d;
    }

    /**
     * Create <code>DTD</code> instance.
     */
    public DTD createDTD(String rootname, ExternalID eid) {
        DTD d = new DTD(rootname, eid);
        d.setFactory(this);
        return d;
    }

    /**
     * Create <code>ElementDecl</code> instance.
     */
    public ElementDecl createElementDecl(String elementname, ContentModel cs) {
        ElementDecl ed = new ElementDecl(elementname, cs);
        ed.setFactory(this);
        return ed;
    }
    /**
     * Create <code>ContentModel</code> instance.
     */
    public ContentModel createContentModel(int type) {
        ContentModel cm = new ContentModel(type);
        cm.setFactory(this);
        return cm;
    }

    /**
     * Create <code>ContentModel</code> instance.
     */
    public ContentModel createContentModel(CMNode cn) {
        ContentModel cm = new ContentModel(cn);
        cm.setFactory(this);
        return cm;
    }

    /**
     * Create <code>Attlist</code> instance.
     */
    public Attlist createAttlist(String elementname) {
        Attlist al = new Attlist(elementname);
        al.setFactory(this);
        return al;
    }
    /**
     * Create <code>AttDef</code> instance.
     */
    public AttDef createAttDef(String attributename) {
        AttDef ad = new AttDef(attributename);
        ad.setFactory(this);
        return ad;
    }


    /**
     * Create <code>Entity</code> instance.
     */
    public Entity createEntity(String name, String value, boolean parameter) {
        Entity en = new Entity(name, value, parameter);
        en.setFactory(this);
        return en;
    }
    /**
     * Create <code>Entity</code> instance.
     */
    public Entity createEntity(String name, ExternalID eid, boolean isParameter, String ndata) {
        Entity en = new Entity(name, eid, isParameter, ndata);
        en.setFactory(this);
        return en;
    }
    /**
     * Create <code>TXNotation</code> instance.
     */
    public TXNotation createNotation(String name, ExternalID eid) {
        TXNotation no = new TXNotation(name, eid);
        no.setFactory(this);
        return no;
    }


    /**
     * Create <code>GeneralReference</code> instance.
     */
    public GeneralReference createGeneralReference(String name) {
        GeneralReference gr = new GeneralReference(name);
        gr.setFactory(this);
        return gr;
    }

    /**
     * Create <code>TXAttributeList</code> instance.
     */
    public TXAttributeList createAttributeList() {
        TXAttributeList al = new TXAttributeList();
        al.setFactory(this);
        return al;
    }

    transient MessageDigest m_digestal = null;
    String m_defaultdigest = "MD5";
    /**
     * Return <code>MessageDigest</code> instance.
     * Default is MD5.
     */
    public MessageDigest createMessageDigest()
        throws java.security.NoSuchAlgorithmException {
        if (null == m_digestal) {
            m_digestal = java.security.MessageDigest.getInstance(m_defaultdigest);
        } else {
            m_digestal.reset();
        }
        return m_digestal;
    }
    /**
     * Change digest algoritm for <CODE>createMessageDigest()</CODE>
     *
     * @see #createMessageDigest
     * @see java.lang.security.MessageDigest#getInstance(java.lang.String)
     */
    public void setDigestAlgorithm(String arg) {
        if (!m_defaultdigest.equals(arg)) {
            m_defaultdigest = arg;
            m_digestal = null;
        }
    }
    /**
     * Calculate a digest value of specified Child instance.
     */
    public byte[] makeDigest(Child ch) throws LibraryException {
        byte[] digest = null;
        synchronized (ch) {
            try {
                MessageDigest md = ch.getFactory().createMessageDigest();
                switch (ch.getNodeType()) {

                  case Node.ELEMENT:
                    TXElement el = (TXElement)ch;
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    DataOutputStream dos = new DataOutputStream(baos);
                    dos.writeInt(Node.ELEMENT);
                    dos.write(el.getTagName().getBytes("UnicodeBigUnmarked"));
                    dos.write((byte)0);
                    dos.write((byte)0);
                                                // a number of Attributes
                    if (null == el.attributes) {
                        dos.writeInt(0);
                    } else {
                        int atlen = el.attributes.getLength();
                        dos.writeInt(atlen);
                                                // Attribute digests
                        String[] as = new String[atlen];
                        for (int i = 0;  i < atlen;  i ++)
                            as[i] = el.attributes.item(i).getName();
                        heapSort(as);
                        for (int i = 0;  i < atlen;  i ++)
                            dos.write(((TXAttribute)el.getAttributeNode(as[i])).getDigest());
                    }

						// a number of children
                    //dos.writeInt(children.getLength());
                                                // children digests
                    NodeIterator ni = el.getChildNodes();
                    Vector v = new Vector();
                    StringBuffer sb = new StringBuffer();
                    Node n;
                    while (null != (n = ni.toNextNode())) {
                        byte[] digest0 = ((Child)n).getDigest();
                        if (n instanceof Text) {
                            sb.append(((Text)n).getData());
                            while (null != n.getNextSibling() && n.getNextSibling() instanceof Text) {
                                n = ni.toNextNode();
                                digest0 = ((Child)n).getDigest();
                                sb.append(((Text)n).getData());
                            }
                            if (0 < sb.length())
                                digest0 = makeTextDigest(el.getFactory().createMessageDigest(),
                                                         sb.toString());
                            sb.setLength(0);
                        }
                        v.addElement(digest0);
                    }
                    dos.writeInt(v.size());
                    for (int i = 0;  i < v.size();  i ++)
                        dos.write((byte[])v.elementAt(i));

                    dos.close();

                    digest = md.digest(baos.toByteArray());
                    break;

                  case Node.ATTRIBUTE:
                    Attribute a = (Attribute)ch;
                    md.update((byte)((Node.ATTRIBUTE >> 24) & 0xff));
                    md.update((byte)((Node.ATTRIBUTE >> 16) & 0xff));
                    md.update((byte)((Node.ATTRIBUTE >> 8) & 0xff));
                    md.update((byte)(Node.ATTRIBUTE & 0xff));
                    md.update(a.getName().getBytes("UnicodeBigUnmarked"));
                    md.update((byte)0);
                    md.update((byte)0);
                    digest = md.digest(a.getValue().getBytes("UnicodeBigUnmarked"));
                    break;

                  case Node.PI:                         // TXPI and NamespacePI and StylesheetPI
                    PI pi = (PI)ch;
                    md.update((byte)((Node.PI >> 24) & 0xff));
                    md.update((byte)((Node.PI >> 16) & 0xff));
                    md.update((byte)((Node.PI >> 8) & 0xff));
                    md.update((byte)(Node.PI & 0xff));
                    md.update(pi.getName().getBytes("UnicodeBigUnmarked"));
                    digest = md.digest(pi.getData().getBytes("UnicodeBigUnmarked"));
                    break;

                  case Node.COMMENT:
                    Comment com = (Comment)ch;
                    md.update((byte)((Node.COMMENT >> 24) & 0xff));
                    md.update((byte)((Node.COMMENT >> 16) & 0xff));
                    md.update((byte)((Node.COMMENT >> 8) & 0xff));
                    md.update((byte)(Node.COMMENT & 0xff));
                    digest = md.digest(com.getData().getBytes("UnicodeBigUnmarked"));
                    break;

                  case Node.TEXT:                       // TXText and TXCDATASection
                    digest = makeTextDigest(md, ((Text)ch).getData());
                    break;

                  case Node.DOCUMENT:
                  case Child.DOCUMENTTYPE:
                  case Child.ELEMENTDEFINITION:
                  case Child.ATTLIST:
                  case Child.ATTDEF:
                  case Child.ENTITY:
                  case Child.NOTATION:
                  case Child.GENERALREFERENCE:
                  case Child.PSEUDONODE:
                    throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#makeDigest(): Digest not implemented for this node type.");
                  default:
                    throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#makeDigest(): Unknown Node type detected.");
                }
            } catch (NoSuchAlgorithmException e0) {
                throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#makeDigest(): "+e0.toString());
            } catch (UnsupportedEncodingException e1) {
                throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#makeDigest(): "+e1.toString());
            } catch (IOException e2) {
                throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#makeDigest(): "+e2.toString());
            } // try-catch
        } // synchronized
        return digest;
    }

    static byte[] makeTextDigest(MessageDigest md, String text) throws UnsupportedEncodingException {
        md.update((byte)((Node.TEXT >> 24) & 0xff));
        md.update((byte)((Node.TEXT >> 16) & 0xff));
        md.update((byte)((Node.TEXT >> 8) & 0xff));
        md.update((byte)(Node.TEXT & 0xff));
        return md.digest(text.getBytes("UnicodeBigUnmarked"));
    }

    static Vector sortStringVector(Vector v) {
        String[] as = new String[v.size()];
        v.copyInto(as);
        heapSort(as);
        v.removeAllElements();
        v.ensureCapacity(as.length);
        for (int i = 0;  i < as.length;  i ++)
            v.addElement(as[i]);
        return v;
    }

    static void heapSort(String[] pd) {
        int i;
        for (i = pd.length/2;  i >= 0;  i--) {  // Make heap
            fall(pd, pd.length, i);
        }
        for (i = pd.length-1;  i > 0;  i--) {
            String t = pd[0];
            pd[0] = pd[i];
            pd[i] = t;
            fall(pd, i, 0);
        }
    }

    static private void fall(String[] pd, int n, int i) {
        int j = 2*i+1;
        if (j < n) {				// left exists
            if (j+1 < n) {			// right exists too
	    					// j: bigger
                if (0 > pd[j].compareTo(pd[j+1]))
                    j = 2*i+2;
            } else {				// only left
            }
            if (0 > pd[i].compareTo(pd[j])) {
                                                // the child is bigger
                String t = pd[i];
                pd[i] = pd[j];
                pd[j] = t;
                fall(pd, n, j);
            }
        }
    }


    /**
     * Make String representation of specified Child instance.
     *
     * @param encoding Java encoding name, or <CODE>null</CODE>
     */
    public String toString(Child ch, String encoding) throws LibraryException {
        String ret = "";
        switch (ch.getNodeType()) {
          case Node.ELEMENT:
            ret = toStringElement((TXElement)ch, encoding);
            break;

          case Node.ATTRIBUTE:
            ret = toStringAttribute((TXAttribute)ch, encoding);
            break;

          case Node.PI:                         // TXPI and NamespacePI and StylesheetPI
            ret = toStringPI((PI)ch, encoding);
            break;

          case Node.COMMENT:
            ret = toStringComment((Comment)ch, encoding);
            break;

          case Node.TEXT:                       // TXText and TXCDATASection
            ret = toStringText((Text)ch, encoding);
            break;

          case Node.DOCUMENT:
            ret = toStringDocument((TXDocument)ch, encoding);
            break;

          case Child.DOCUMENTTYPE:
            ret = toStringDTD((DTD)ch, encoding);
            break;

          case Child.ELEMENTDEFINITION:
            ret = toStringElementDecl((ElementDecl)ch, encoding);
            break;

          case Child.ATTLIST:
            ret = toStringAttlist((Attlist)ch, encoding);
            break;

          case Child.ATTDEF:
            ret = toStringAttDef((AttDef)ch, encoding);
            break;

          case Child.ENTITY:
            ret = toStringEntity((Entity)ch, encoding);
            break;

          case Child.NOTATION:
            ret = toStringNotation((TXNotation)ch, encoding);
            break;

          case Child.GENERALREFERENCE:
            ret = toStringGeneralReference((GeneralReference)ch, encoding);
            break;

          case Child.PSEUDONODE:
            ret = toStringPseudoNode((PseudoNode)ch, encoding);
            break;

          default:
            throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#toString(): Unknown Node type detected.");
        }
        if (null == ret) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            ch.print(pw, null);
            pw.close();
            ret = sw.toString();
        }
        return ret;
    }
    public String toStringDocument(TXDocument doc, String encoding) {
        return "";
    }
    public String toStringElement(TXElement el, String encoding) {
        return null;
    }
    public String toStringAttribute(TXAttribute attr, String encoding) {
        return attr.getValue();                // This is defined by DOM.
    }
    public String toStringPI(PI pi, String encoding) {
        return null;
    }
    public String toStringComment(Comment com, String encoding) {
        return null;
    }
    public String toStringText(Text text, String encoding) {
        String ret;
        if (text instanceof CDATASection) {
            ret = "<![CDATA["+text.getData()+"]]>";
        } else {
            ret = Util.backReference(text.getData(), encoding);
        }
        return ret;
    }
    public String toStringDTD(DTD dtd, String encoding) {
        return null;
    }
    public String toStringElementDecl(ElementDecl ed, String encoding) {
        return null;
    }
    public String toStringAttlist(Attlist al, String encoding) {
        return null;
    }
    public String toStringAttDef(AttDef ad, String encoding) {
        return null;
    }
    public String toStringEntity(Entity ent, String encoding) {
        return null;
    }
    public String toStringNotation(TXNotation no, String encoding) {
        return null;
    }
    public String toStringGeneralReference(GeneralReference gr, String encoding) {
        return "&"+gr.getName()+";";
    }
    public String toStringPseudoNode(PseudoNode pn, String encoding) {
        return pn.getData();
    }

    /**
     * Print a child to a stream.
     *
     * @param encoding Java encoding name, or <CODE>null</CODE>
     */
    public void print(Child ch, PrintWriter pw, String encoding) throws LibraryException {
        switch (ch.getNodeType()) {

          case Node.DOCUMENT:
            printDocument((TXDocument)ch, pw, encoding);
            break;
            
          case Node.ELEMENT:
            printElement((TXElement)ch, pw, encoding);
            break;
            
          case Node.ATTRIBUTE:
            // Notiong to do.  Attributes are printed in Elements.
            break;

          case Node.PI:                         // TXPI and NamespacePI and StylesheetPI
            printPI((PI)ch, pw, encoding);
            break;

          case Node.COMMENT:
            printComment((Comment)ch, pw, encoding);
            break;

          case Node.TEXT:                       // TXText and TXCDATASection
            printText((Text)ch, pw, encoding);
            break;

          case Child.DOCUMENTTYPE:
            printDTD((DTD)ch, pw, encoding);
            break;

          case Child.ELEMENTDEFINITION:
            printElementDecl((ElementDecl)ch, pw, encoding);
            break;

          case Child.ATTLIST:
            printAttlist((Attlist)ch, pw, encoding);
            break;

          case Child.ATTDEF:
            printAttDef((AttDef)ch, pw, encoding);
            break;

          case Child.ENTITY:
            printEntity((Entity)ch, pw, encoding);
            break;

          case Child.NOTATION:
            printNotation((TXNotation)ch, pw, encoding);
            break;

          case Child.GENERALREFERENCE:
            printGeneralReference((GeneralReference)ch, pw, encoding);
            break;

          case Child.PSEUDONODE:
            printPseudoNode((PseudoNode)ch, pw, encoding);
            break;

          default:
            throw new LibraryException("com.ibm.xml.parser.DefaultElementFactory#print(): Unknown Node type detected.");
        }
    }

    public void printDocument(TXDocument doc, PrintWriter pw, String encoding) {
        if (null != doc.getVersion()) {
            pw.print("<?xml version=\"");
            pw.print(doc.getVersion());
            pw.print("\"");
            if (null != doc.getEncoding()) {
                pw.print(" encoding=\"");
                pw.print(doc.getEncoding());
                pw.print("\"");
            }
            if (null != doc.getStandalone()) {
                pw.print(" standalone=\"");
                pw.print(doc.getStandalone());
                pw.print("\"");
            }
            pw.print("?>");
        }
        Enumeration en = doc.elements();
        while (en.hasMoreElements()) {
            ((Child)en.nextElement()).print(pw, encoding);
        }
        if (null == doc.getDocumentElement())
            pw.println("<!-- The document has no root element. -->");
        pw.flush();
    }
    public void printElement(TXElement el, PrintWriter pw, String encoding) {
                                                // Print `<' and name and attributes
        pw.print(el.makeHead(encoding).toString());
                                                // Print `>' and contents and end-tag
        Child c = (Child)el.getFirstChild();
        if (el.isEmpty()) {
            pw.print("/>");

        } else {
            pw.print(">");
            while (null != c) {
                c.print(pw, encoding);
                c = (Child)c.getNextSibling();
            }
            pw.print("</");
            pw.print(el.getName());
            pw.print(">");
        }
    }
    /**
     * Printer for <CODE>TXPI</CODE> and <CODE>NamespacePI</CODE> and <CODE>StylesheetPI</CODE>.
     */
    public void printPI(PI pi, PrintWriter pw, String encoding) {
        pw.print("<?");
        pw.print(pi.getName());
        pw.print(pi.getData());
        pw.print("?>");
    }
    public void printComment(Comment com, PrintWriter pw, String encoding) {
        pw.print("<!--");
        pw.print(com.getData());
        pw.print("-->");
    }
    /**
     * Printer for <CODE>TXText</CODE> and <CODE>TXCDATASection</CODE>.
     */
    public void printText(Text text, PrintWriter pw, String encoding) {
        if (text instanceof CDATASection) {
            pw.print("<![CDATA[");
            pw.print(text.getData());
            pw.print("]]>");
        } else {
            pw.print(Util.backReference(text.getData(), encoding));
        }
    }
    public void printDTD(DTD dtd, PrintWriter pw, String encoding) {
        pw.print("<!DOCTYPE ");
        pw.print(dtd.getName());
        pw.print(" ");
        if (null != dtd.getExternalID()) {
            pw.print(dtd.getExternalID());
            pw.print(" ");
        }
        if (dtd.isPrintInternalDTD() && 0 < dtd.getInternalSize()) {
            pw.print("[");
            Enumeration enu = dtd.internalElements();
            while (enu.hasMoreElements())
                ((Child)enu.nextElement()).print(pw, encoding);
            pw.print("]");
        }
        pw.print(">");
    }
    public void printElementDecl(ElementDecl ed, PrintWriter pw, String encoding) {
        pw.print("<!ELEMENT ");
        pw.print(ed.getName());
        pw.print(" ");
        pw.print(ed.getXML4JContentModel());
        pw.print(">");
    }
    public void printAttlist(Attlist al, PrintWriter pw, String encoding) {
        pw.print("<!ATTLIST ");
        pw.print(al.getName());
        int asize = al.size();
        if (0 >= asize) {
            pw.print(" >");
        } else if (1 == asize) {
            pw.print(al.elementAt(0));
            pw.print((char)'>');
        } else {
            pw.println("");
            for (int i = 0;  i < asize;  i ++) {
                Format.printSpace(pw, 4);
                al.elementAt(i).print(pw, encoding);
                if (i == asize-1) {
                    pw.print(">");
                } else {
                    pw.println("");
                }
            }
        }
    }
    public void printAttDef(AttDef ad, PrintWriter pw, String encoding) {
        pw.print((char)' ');
        pw.print(ad.getName());
        pw.print((char)' ');
        if (AttDef.NAME_TOKEN_GROUP != ad.getDeclaredType()) {
            pw.print(AttDef.S_TYPESTR[ad.getDeclaredType()]);
            pw.print((char)' ');
        }
        int type = ad.getDeclaredType();
        if (AttributeDefinition.NOTATION == type
            || AttributeDefinition.NAME_TOKEN_GROUP == type) {
            pw.print((char)'(');
            pw.print(ad.elementAt(0));
            for (int i = 1;  i < ad.size();  i ++) {
                pw.print((char)'|');
                pw.print(ad.elementAt(i));
            }
            pw.print(") ");
        }

        int deft = ad.getDefaultType();
        if (AttDef.REQUIRED == deft) {
            pw.print("#REQUIRED");
        } else if (AttDef.IMPLIED == deft) {
            pw.print("#IMPLIED");
        } else if (AttDef.FIXED == deft) {
            pw.print("#FIXED \"");
            pw.print(null == ad.getDefaultStringValue()
                     ? "(null)" : Util.backReference(ad.getDefaultStringValue(), encoding));
            pw.print((char)'"');
        } else if (AttDef.NOFIXED == deft) {
            pw.print((char)'"');
            pw.print(null == ad.getDefaultStringValue()
                     ? "(null)" : Util.backReference(ad.getDefaultStringValue(), encoding));
            pw.print((char)'"');
        }
    }    
    public void printEntity(Entity ent, PrintWriter pw, String encoding) {
        pw.print("<!ENTITY ");
        if (ent.isParameter())  pw.print("% ");
        pw.print(ent.getName()+" ");
        if (null != ent.getValue()) {
            pw.print("\"");
            pw.print(Util.backReferenceForEntity(ent.getValue(), encoding));
            pw.print("\">");
        } else {
            pw.print(ent.getExternalID());
            if (null != ent.getNDATAType()) {
                pw.print(" NDATA ");
                pw.print(ent.getNDATAType());
            }
            pw.print(">");
        }
    }
    public void printNotation(TXNotation no, PrintWriter pw, String encoding) {
        pw.print("<!NOTATION ");
        pw.print(no.getName());
        pw.print(" ");
        pw.print(no.getExternalID());
        pw.print(">");
    }
    public void printGeneralReference(GeneralReference gr,
                                      PrintWriter pw, String encoding) {
        pw.print((char)'&');
        pw.print(gr.getName());
        pw.print((char)';');
    }
    public void printPseudoNode(PseudoNode pn, PrintWriter pw, String encoding) {
        pw.print(pn.getData());
    }
}
