private Node generateFieldNode(Document doc, ClassField field) {
    Element fieldEl = doc.createElement(EL_FIELD);

    fieldEl.setAttribute(ATR_NAME, field.getName());
    fieldEl.setAttribute(ATR_TYPE, field.getType());

    if (field.isInput()) fieldEl.setAttribute(ATR_NATURE, "input");
    else if (field.isGoal()) fieldEl.setAttribute(ATR_NATURE, "goal");

    if (field.getValue() != null) fieldEl.setAttribute(ATR_VALUE, field.getValue());

    return fieldEl;
  }
  /** Method called at the end of the class. */
  public void visitEnd() {
    AbstractClassMetaData cmd = enhancer.getClassMetaData();
    if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE) {
      // Add any new fields
      List fields = enhancer.getFieldsList();
      Iterator fieldsIter = fields.iterator();
      while (fieldsIter.hasNext()) {
        ClassField field = (ClassField) fieldsIter.next();
        if (field.getName().equals(enhancer.getNamer().getDetachedStateFieldName())
            && hasDetachedState) {
          // No need to add this field since exists
          continue;
        }

        if (DataNucleusEnhancer.LOGGER.isDebugEnabled()) {
          DataNucleusEnhancer.LOGGER.debug(
              Localiser.msg("005021", ((Class) field.getType()).getName() + " " + field.getName()));
        }
        cv.visitField(
            field.getAccess(),
            field.getName(),
            Type.getDescriptor((Class) field.getType()),
            null,
            null);
      }

      if (!hasStaticInitialisation) {
        // Add a static initialisation block for the class since nothing added yet
        InitClass method = InitClass.getInstance(enhancer);
        method.initialise(cv);
        method.execute();
        method.close();
      }

      if (!hasDefaultConstructor
          && enhancer.hasOption(ClassEnhancer.OPTION_GENERATE_DEFAULT_CONSTRUCTOR)) {
        // Add a default constructor
        DefaultConstructor ctr = DefaultConstructor.getInstance(enhancer);
        ctr.initialise(cv);
        ctr.execute();
        ctr.close();
      }

      // Add any new methods
      List methods = enhancer.getMethodsList();
      Iterator<ClassMethod> methodsIter = methods.iterator();
      while (methodsIter.hasNext()) {
        ClassMethod method = methodsIter.next();
        method.initialise(cv);
        method.execute();
        method.close();
      }

      if (Serializable.class.isAssignableFrom(enhancer.getClassBeingEnhanced())) {
        // Class is Serializable
        if (!hasSerialVersionUID) {
          // Needs "serialVersionUID" field
          Long uid = null;
          try {
            uid =
                (Long)
                    AccessController.doPrivileged(
                        new PrivilegedAction() {
                          public Object run() {
                            return Long.valueOf(
                                ObjectStreamClass.lookup(enhancer.getClassBeingEnhanced())
                                    .getSerialVersionUID());
                          }
                        });
          } catch (Throwable e) {
            DataNucleusEnhancer.LOGGER.warn(StringUtils.getStringFromStackTrace(e));
          }
          ClassField cf =
              new ClassField(
                  enhancer,
                  enhancer.getNamer().getSerialVersionUidFieldName(),
                  Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL,
                  long.class,
                  uid);
          if (DataNucleusEnhancer.LOGGER.isDebugEnabled()) {
            DataNucleusEnhancer.LOGGER.debug(
                Localiser.msg("005021", ((Class) cf.getType()).getName() + " " + cf.getName()));
          }
          cv.visitField(
              cf.getAccess(),
              cf.getName(),
              Type.getDescriptor((Class) cf.getType()),
              null,
              cf.getInitialValue());
        }

        // The dnPreSerialize method need be called only once for a persistent instance. The
        // writeObject method in the least-derived
        // pc class that implements Serializable in the inheritance hierarchy needs to be modified
        // or generated to call it.
        if (cmd.getSuperAbstractClassMetaData() == null && !hasWriteObject) {
          // User hasn't provided their own writeObject, so provide the default but with a call to
          // dnPreSerialize first
          ClassMethod method = WriteObject.getInstance(enhancer);
          method.initialise(cv);
          method.execute();
          method.close();
        }
      }

      // Add dnGetXXX, dnSetXXX for each of the (managed) fields/properties
      AbstractMemberMetaData[] fmds = cmd.getManagedMembers();
      for (int i = 0; i < fmds.length; i++) {
        if (fmds[i].getPersistenceModifier() == FieldPersistenceModifier.NONE) {
          // Field/Property is not persistent so ignore
          continue;
        }

        byte persistenceFlags = fmds[i].getPersistenceFlags();
        ClassMethod getMethod = null;
        ClassMethod setMethod = null;
        if (fmds[i] instanceof PropertyMetaData) {
          // dnGetXXX, dnSetXXX for property are generated when processing existing getXXX, setXXX
          // methods
        } else {
          // Generate dnGetXXX, dnSetXXX for field
          if ((persistenceFlags & Persistable.MEDIATE_READ) == Persistable.MEDIATE_READ) {
            getMethod = new GetViaMediate(enhancer, fmds[i]);
          } else if ((persistenceFlags & Persistable.CHECK_READ) == Persistable.CHECK_READ) {
            getMethod = new GetViaCheck(enhancer, fmds[i]);
          } else {
            getMethod = new GetNormal(enhancer, fmds[i]);
          }

          if ((persistenceFlags & Persistable.MEDIATE_WRITE) == Persistable.MEDIATE_WRITE) {
            setMethod = new SetViaMediate(enhancer, fmds[i]);
          } else if ((persistenceFlags & Persistable.CHECK_WRITE) == Persistable.CHECK_WRITE) {
            setMethod = new SetViaCheck(enhancer, fmds[i]);
          } else {
            setMethod = new SetNormal(enhancer, fmds[i]);
          }
        }

        if (getMethod != null) {
          getMethod.initialise(cv);
          getMethod.execute();
          getMethod.close();
        }
        if (setMethod != null) {
          setMethod.initialise(cv);
          setMethod.execute();
          setMethod.close();
        }
      }
    }
    cv.visitEnd();
  }
  private void parsePort(PackageClass newClass, Element portNode) {
    String name = portNode.getAttribute(ATR_NAME);
    String type = portNode.getAttribute(ATR_TYPE);
    String x = portNode.getAttribute(ATR_X);
    String y = portNode.getAttribute(ATR_Y);
    String portConnection = portNode.getAttribute(ATR_PORT_CONNECTION);
    String strict = portNode.getAttribute(ATR_STRICT);
    String multi = portNode.getAttribute(ATR_MULTI);

    ClassField cf = newClass.getSpecField(name);

    if (newClass.getComponentType().hasSpec()) {

      if (name.indexOf(".") > -1) {
        // TODO - temporarily do not dig into hierarchy
        int idx = name.indexOf(".");
        String root = name.substring(0, idx);

        if (newClass.getSpecField(root) == null) {
          collector.collectDiagnostic(
              "Field "
                  + root
                  + " in class "
                  + newClass.getName()
                  + " is not declared in the specification, variable "
                  + type
                  + " "
                  + name
                  + " ignored ");
          return;
        }

        newClass.addSpecField(new ClassField(name, type));
      } else if (!TypeUtil.TYPE_THIS.equalsIgnoreCase(name)) {
        if (cf == null) {

          collector.collectDiagnostic(
              "Port "
                  + type
                  + " "
                  + name
                  + " in class "
                  + newClass.getName()
                  + " does not have the corresponding field in the specification");
        } else if (!cf.getType().equals(type)
            // type may be declared as "alias", however cf.getType() returns e.g. "double[]", ignore
            // it
            && !(cf.isAlias() && TypeUtil.TYPE_ALIAS.equals(type))) {

          collector.collectDiagnostic(
              "Port "
                  + type
                  + " "
                  + name
                  + " in class "
                  + newClass.getName()
                  + " does not match the field declared in the specification: "
                  + cf.getType()
                  + " "
                  + cf.getName());
        }
      }
    }

    Port newPort =
        new Port(
            name,
            type,
            Integer.parseInt(x),
            Integer.parseInt(y),
            portConnection,
            Boolean.parseBoolean(strict),
            Boolean.parseBoolean(multi));

    if (portNode.hasAttribute(ATR_ID)) newPort.setId(portNode.getAttribute(ATR_ID));

    Element gr;
    // open
    if ((gr = getElementByName(portNode, EL_OPEN)) != null
        && (gr = getElementByName(gr, EL_GRAPHICS)) != null) {
      newPort.setOpenGraphics(getGraphicsParser().parse(gr));
    }

    // closed
    if ((gr = getElementByName(portNode, EL_CLOSED)) != null
        && (gr = getElementByName(gr, EL_GRAPHICS)) != null) {
      newPort.setClosedGraphics(getGraphicsParser().parse(gr));
    }

    newClass.addPort(newPort);
  }
  private void parseField(PackageClass newClass, Element fieldNode) {
    String name = fieldNode.getAttribute(ATR_NAME);
    String type = fieldNode.getAttribute(ATR_TYPE);

    ClassField newField;

    if (newClass.getComponentType().hasSpec()) {
      if (name.indexOf(".") > -1) {
        // TODO - temporarily do not dig into hierarchy
        int idx = name.indexOf(".");
        String root = name.substring(0, idx);

        if (newClass.getSpecField(root) == null) {
          collector.collectDiagnostic(
              "Field "
                  + root
                  + " in class "
                  + newClass.getName()
                  + " is not declared in the specification, variable "
                  + type
                  + " "
                  + name
                  + " ignored ");
          return;
        }

        newField = new ClassField(name, type);
        newClass.addSpecField(newField);
      } else {
        newField = newClass.getSpecField(name);

        if (newField == null) {

          collector.collectDiagnostic(
              "Field "
                  + type
                  + " "
                  + name
                  + " in class "
                  + newClass.getName()
                  + " is not declared in the specification");
          return;
        } else if (!newField.getType().equals(type)) {

          collector.collectDiagnostic(
              "Field "
                  + type
                  + " "
                  + name
                  + " in class "
                  + newClass.getName()
                  + " does not match the field declared in the specification: "
                  + newField.getType()
                  + " "
                  + newField.getName());
          return;
        }
      }
    } else {
      newField = new ClassField(name, type);
      newClass.addSpecField(newField);
    }

    newField.setValue(fieldNode.hasAttribute(ATR_VALUE) ? fieldNode.getAttribute(ATR_VALUE) : null);
    newField.setDescription(fieldNode.getAttribute(ATR_DESCRIPTION));
    newField.setHidden(Boolean.parseBoolean(fieldNode.getAttribute(ATR_HIDDEN)));

    String nature = fieldNode.getAttribute(ATR_NATURE);
    if ("input".equals(nature)) newField.setInput(true);
    else if ("goal".equals(nature)) newField.setGoal(true);

    newClass.addField(newField);

    Element gr;
    // known
    if ((gr = getElementByName(fieldNode, EL_KNOWN)) != null
        && (gr = getElementByName(gr, EL_GRAPHICS)) != null) {
      newField.setKnownGraphics(getGraphicsParser().parse(gr));
    }
    // default
    if ((gr = getElementByName(fieldNode, EL_DEFAULT)) != null
        && (gr = getElementByName(gr, EL_GRAPHICS)) != null) {
      newField.setDefaultGraphics(getGraphicsParser().parse(gr));
    }
  }