## defs/ClassDef.java

/* [<][>][^][v][top][bottom][index][help] */

## FUNCTIONS

This source file includes following functions.

1. ClassDef
2. getNodeValue
3. createAttributes
4. createForeignAttrs
5. getName
6. getCode
7. getStatus
8. getDbaseCode
9. getWidth
10. getAttributes
11. getForeignAttrs
12. getEnum
13. getTemplate
14. getTemplateV
15. getDiagram
16. toString
17. ClassDiagram
18. toString

    import java.util.*;
    import org.w3c.dom.*;
    import com.sun.xml.tree.*;

    /**
     * RIPE class.
     *
     * @author ottrey@ripe.net
     * @version $Version$
     *
     */
    public class ClassDef {
    /* [<][>][^][v][top][bottom][index][help] */

      public static final int EXTRA_BIT=11;

      private String    name;
      private String    code;
      private String    status;
      private String    description;
      private Vector    attributes;
      private int       dbaseCode;

      private int       width;        // The longest attribute name.
      private Hashtable foreignAttrs;

      // -----------------oOo-----------------
      //              Constructors
      // -----------------oOo-----------------
      /**
       * Creates a RIPE class.
       *
       * @author ottrey@ripe.net
       * @version $Version$
       *
       * @param obj The node from which a RIPE class is made.
       *
       * NOTE: No-one has implemented a Node.getNodeValue() method yet.  ;-(
       *       description = tw.getNextElement("description").getNodeValue();
       *       So I wrote a crappy one here.
       * @see getNodeValue
       */
      public ClassDef(Node obj, Hashtable ripeAttributes) {
        width = 0;
        name      = obj.getAttributes().getNamedItem("name").getNodeValue();
        code      = obj.getAttributes().getNamedItem("code").getNodeValue();
        status    = obj.getAttributes().getNamedItem("status").getNodeValue();

        // Prepare to walk the tree.
        TreeWalker tw = new TreeWalker(obj);

        // Extract the "description".
        description = getNodeValue(tw.getNextElement("description"));

        // Create the attributes.
        attributes = createAttributes(tw.getNextElement("attributes"), ripeAttributes);

        // Get the dbase_code.
        dbaseCode = -1;
        Node dcn = tw.getNextElement("dbase_code");
        if (dcn != null) {
          String dbaseCodeStr = dcn.getAttributes().getNamedItem("value").getNodeValue();
          try {
            dbaseCode = Integer.parseInt(dbaseCodeStr);
          }
          catch (NumberFormatException e) {
            System.err.println("Bad dbaseCode: " + dbaseCodeStr);
            System.err.println("\tencounterd in Object: " + obj);
            System.exit(-1);
          }
        }

        // Create the foreignAttrs.
        foreignAttrs = createForeignAttrs();

      } // ClassDef()

      /**
       * Aaaargh I shouldn't have to write this. :-(
       *
       * @param        node
       * @return       The value of the node.
       * @see          ClassDef
       *
       */
      private String getNodeValue(Node node) {
    /* [<][>][^][v][top][bottom][index][help] */
        String nodeStr = node.toString();
        int startIndex = nodeStr.indexOf('>') + 1;
        int endIndex = nodeStr.lastIndexOf('<') - 1;

        return nodeStr.substring(startIndex, endIndex);
      } // getNodeValue()

      /**
       * Create the attributes for this class from the attributes already created.
       *
       * @param  attrs          The head node of the attributes tree from the classes DOM.
       * @param  ripeAttributes The attributes created from the attributes DOM.
       * @return       (methods only)
       *
       */
      private Vector createAttributes(Node attrs, Hashtable ripeAttributes) {
    /* [<][>][^][v][top][bottom][index][help] */
        // Create a set of attributes for this class.
        Vector result = new Vector();

        Node attrNode;
        AttributeDef adTmp;
        String attrCode=null;
        String status;

        // Prepare to walk the tree.
        TreeWalker tw = new TreeWalker(attrs);
        try {
          // Get the attribute node from the tree.
          while ((attrNode = tw.getNext()) != null) {
            // Get the attribute code from the node.
            attrCode = new String(attrNode.getNodeName());

            // Get the attribute from the ripeAttributes.
            adTmp = (AttributeDef)ripeAttributes.get(attrCode);

            // Keep track of the longest attribute name.
            if ( adTmp.getName().length() > width ) {
              width = adTmp.getName().length();
            }

            // If the attribute is "valid"
            if ((status=adTmp.getStatus()).equals("valid")) {

              // Create a clone of the attribute.
              AttributeDef ad = null;
              try {
                ad = (AttributeDef)adTmp.clone();
              }
              catch (CloneNotSupportedException e) {
                System.err.println("Doh!");
              }

              // Set the min attribute.
              ad.setChoice(attrNode.getAttributes().getNamedItem("choice").getNodeValue());

              // Set the max attribute.
              ad.setNumber(attrNode.getAttributes().getNamedItem("number").getNodeValue());

              // Add the attribute to this class.
              result.addElement(ad);
            }
            else {
              System.err.println("Warning: Class " + name + " contains a " +
                                 status + " attribute: " + attrCode);
            }
          }
        }
        catch (NullPointerException e) {
          // We tried to do something with a null - No worries, we caught it!
          System.err.println("Trouble creating attributes: " + e);
          System.err.println("attrCode=" + attrCode);
          System.err.println("attrs=" + attrs);
        }

        return result;
      } // createAttributes()

      private Hashtable createForeignAttrs() {
    /* [<][>][^][v][top][bottom][index][help] */
        // Create a set of foreign attributes for this class.
        Hashtable result = new Hashtable();

        Enumeration e = attributes.elements();
        while (e.hasMoreElements()) {
          AttributeDef ad = (AttributeDef)e.nextElement();
          String code = ad.getCode();
          String foreign = ad.getForeign();
          if (foreign.length() > 1 ) {
            result.put(code, foreign);
          }
        }
        return result;
      } // createAttributes()

      public String getName() {
    /* [<][>][^][v][top][bottom][index][help] */
        return name;
      } // getCode()

      public String getCode() {
    /* [<][>][^][v][top][bottom][index][help] */
        return code;
      } // getCode()

      public String getStatus() {
    /* [<][>][^][v][top][bottom][index][help] */
        return status;
      } // getStatus()

      public int getDbaseCode() {
    /* [<][>][^][v][top][bottom][index][help] */
        return dbaseCode;
      } // getDbaseCode()

      public int getWidth() {
    /* [<][>][^][v][top][bottom][index][help] */
        return width + EXTRA_BIT;
      } // getWidth()

      public Vector getAttributes() {
    /* [<][>][^][v][top][bottom][index][help] */
        return attributes;
      } // getAttributes()

      public Hashtable getForeignAttrs() {
    /* [<][>][^][v][top][bottom][index][help] */
        return foreignAttrs;
      } // getForeignAttrs()

      public String getEnum() {
    /* [<][>][^][v][top][bottom][index][help] */
        return new String("C_" + code).toUpperCase();
      } // getEnum()

     /**
      * Returns a template for the class in the form:
      *
      * person:      [mandatory]  [single]     [primary/look-up key]
      * address:     [mandatory]  [multiple]   [ ]
      * phone:       [mandatory]  [multiple]   [ ]
      * fax-no:      [optional]   [multiple]   [ ]
      * e-mail:      [optional]   [multiple]   [look-up key]
      * nic-hdl:     [mandatory]  [single]     [primary/look-up key]
      * remarks:     [optional]   [multiple]   [ ]
      * notify:      [optional]   [multiple]   [inverse key]
      * mnt-by:      [optional]   [multiple]   [inverse key]
      * changed:     [mandatory]  [multiple]   [ ]
      * source:      [mandatory]  [single]     [ ]
      *
      * @return String the template.
      */
      public String getTemplate() {
    /* [<][>][^][v][top][bottom][index][help] */
        String result = new String();

        String pad1 = "            ";
        String pad2 = "           ";
        String pad3 = "           ";

        AttributeDef ad;
        String name, choice, number, keytype;
        Enumeration e = attributes.elements();
        while (e.hasMoreElements()) {
          ad = (AttributeDef)e.nextElement();
          name = ad.getName();
          choice = ad.getChoice();
          number = ad.getNumber();
          keytype = ad.getKeytype2();
          result += (name + ":"                                        +
                     pad1.substring(0, pad1.length()-name.length())    +
                     "[" + choice + "]"                                +
                     pad2.substring(0, pad2.length()-choice.length())  +
                     "[" + number + "]"                                +
                     pad3.substring(0, pad3.length()-number.length())  +
                     "[" + keytype + "]"                               +
                     "\n");
        }

        return result;
      } // getTemplate()

      public String getTemplateV() {
    /* [<][>][^][v][top][bottom][index][help] */
        String result=new String();
        String templ = getTemplate();

        result += ("The " + name + " class:\n\n"          +
                   description + "\n\n"                    +
                   templ + "\n"                            +
                   "The content of the attributes of the " +
                   name + "class are defined below:"      +
                   "\n\n");

        AttributeDef ad;
        String name, description, format;
        Enumeration e = attributes.elements();
        while (e.hasMoreElements()) {
          ad = (AttributeDef)e.nextElement();
          name = ad.getName();
          description = ad.getDescription();
          format = ad.getFormat();

          result += (name + description + format + "\n");
        }

        result += ("\n"                                             +
                   "Further information may be found at:\n"         +
                   "\n"                                             +
                   "http://www.ripe.net/docs/ripe-157.html\n"     +
                   "ftp://ftp.ripe.net/ripe/docs/ripe-157.ps\n"   +
                   "ftp://ftp.ripe.net/ripe/docs/ripe-157.txt\n"  +
                   "\n");

        return result;
      } // getTemplateV()

      public String getDiagram(int maxWidth, Hashtable foreigns) {
    /* [<][>][^][v][top][bottom][index][help] */
        ClassDiagram om = new ClassDiagram(this, maxWidth, foreigns);

        return om.toString();
      } // getDiagram()

      /*
      public boolean equals(ClassDef ro) {
        return code.equals(ro.getCode());
      } // equals()
      */

      public String toString() {
    /* [<][>][^][v][top][bottom][index][help] */
        return new String("ripe class={" +
                             "\n\tname="         + name          +
                             "\n\tcode="         + code          +
                             "\n\tstatus="       + status        +
                             "\n\tdescription="  + description   +
                             "\n\tattributes="   + attributes    +
                             "\n\tdbaseCode="    + dbaseCode     +
                             "\n\twidth="        + width         +
                             "\n\tforeignAttrs=" + foreignAttrs  +
                             "\n}");
      } // toString()

      /**
       * RIPE class diagram.
       *
       * @author ottrey@ripe.net
       * @version $Version$
       *
       */
      public class ClassDiagram {
    /* [<][>][^][v][top][bottom][index][help] */
        private Vector    diagram;

        // -----------------oOo-----------------
        //              Constructors
        // -----------------oOo-----------------
        /**
         * Creates a RIPE class diagram.
         *
         * @author ottrey@ripe.net
         * @version $Version$
         *
         * @param od Definition to make diagram from.
         */
        public ClassDiagram(ClassDef od, int maxWidth, Hashtable foreigns) {
          diagram = new Vector();
          String line = new String();

          String border = new String();
          border = "+-";
          for (int i=0; i < getWidth(); i++) {
            border += ("-");
          }
          border += "-+   ";
          for (int i=od.getWidth(); i < maxWidth; i++) {
            border += (" ");
          }
          Enumeration e1 = foreigns.keys();
          while (e1.hasMoreElements()) {
            String foreign = (String)e1.nextElement();
            if (e1.hasMoreElements()) {
              border += ("|--");
            }
          }
          border += ("|\n");
          diagram.addElement(border);

          line = ("| " + od.getCode() + ": " + od.getName());
          for (int i=od.getName().length()+4; i < getWidth(); i++) {
            line += (" ");
          }
          line += (" |   ");
          for (int i=od.getWidth(); i < maxWidth; i++) {
            line += (" ");
          }
          Enumeration e3 = foreigns.keys();
          while (e3.hasMoreElements()) {
            String foreign = (String)e3.nextElement();
            line += (foreign + " ");
          }
          line += ("\n");
          diagram.addElement(line);

          diagram.addElement(border);

          AttributeDef ad;
          Enumeration e = od.getAttributes().elements();
          while (e.hasMoreElements()) {
            ad = (AttributeDef)e.nextElement();
            String name = ad.getName();
            String keytype = ad.getKeytype3();
            line = ("| " + ad.getCode() + ": " + name + " ");
            for (int i=name.length(); i < width; i++) {
              line += (" ");
            }
            line += (keytype + " |");

            // The horizontal line.
            // Is a foreign key.
            boolean f = (ad.getForeign().length() > 1);
            // Is a referenced key.
            boolean p = (foreigns.contains(ad.getCode()));
            if (f) {
              line += ("->-");
            }
            else if (p) {
              line += ("-|-");
            } else {
              line += ("   ");
            }
            for (int i=od.getWidth(); i < maxWidth; i++) {
              if (f || p) {
                line += ("-");
              }
              else {
                line += (" ");
              }
            }

            // Add the xrefs.
            Enumeration e2 = foreigns.keys();
            while (e2.hasMoreElements()) {
              String foreign = (String)e2.nextElement();
              String code = ad.getCode();
              if (foreign.equals(code)) {
                line += ("X");
              }
              else if (foreigns.get(foreign).equals(code)) {
                line += ("o");
              }
              else {
                line += ("|");
              }
              if (e2.hasMoreElements()) {
                line += ("--");
              }
            }
            line += ("\n");
            diagram.addElement(line);
          }

          diagram.addElement(border);
        } // ClassDiagram()

        public String toString() {
    /* [<][>][^][v][top][bottom][index][help] */
          String result = new String();

          Enumeration e = diagram.elements();
          while (e.hasMoreElements()) {
            String line = (String)e.nextElement();
            result += line;
          }

          return result;
        } // toString()

      } // ClassDiagram

    } // ClassDef

/* [<][>][^][v][top][bottom][index][help] */