/* (non-Javadoc)
   * @see org.jibx.binding.model.ElementBase#validate(org.jibx.binding.model.ValidationContext)
   */
  public void validate(ValidationContext vctx) {

    // call base class method first
    super.validate(vctx);

    // check for way to determine if named collection present on output
    if (vctx.isOutBinding()
        && hasName()
        && !hasProperty()
        && isOptional()
        && getTest() == null
        && !(vctx.getParentContainer() instanceof CollectionElement)) {
      vctx.addError("Need test method for optional collection output element");
    }

    // make sure only named element components are present
    ArrayList children = children();
    for (int i = 0; i < children.size(); i++) {
      IComponent child = (IComponent) children.get(i);
      if (child.hasAttribute()) {
        vctx.addFatal("Attributes not allowed as child components of collection", child);
      }
    }

    // check each child component
    if (m_itemTypeClass != null) {
      checkCollectionChildren(vctx, m_itemTypeClass, children);
    }

    // make sure child components can be distinguished
    if (children.size() > 1) {
      if (isOrdered()) {
        checkOrderedChildren(vctx, children);
      } else {
        checkUnorderedChildren(vctx, children);
      }
    }
  }
  /**
   * Check children of unordered collection for consistency. In an input binding each child element
   * must define a unique qualified name. In an output binding each child element must define a
   * unique type or supply a test method to allow checking when that element should be generated for
   * an object.
   *
   * @param vctx validation context
   * @param children list of child components
   */
  private void checkUnorderedChildren(ValidationContext vctx, ArrayList children) {
    HashMap typemap = new HashMap();
    HashMap namemap = new HashMap();
    for (int i = 0; i < children.size(); i++) {
      ElementBase child = (ElementBase) children.get(i);
      if (child instanceof IComponent && !vctx.isSkipped(child)) {
        IComponent comp = (IComponent) child;
        if (vctx.isInBinding()) {

          // make sure name supplied for unmarshalling
          if (!comp.hasName()) {
            vctx.addFatal(
                "Child components of collection must " + "define element name for unmarshalling",
                comp);
          }

          // names must be distinct in input binding
          String name = comp.getName();
          String uri = comp.getUri();
          if (uri == null) {
            uri = "";
          }
          Object value = namemap.get(name);
          if (value == null) {

            // first instance of name, store directly
            namemap.put(name, comp);

          } else if (value instanceof HashMap) {

            // multiple instances already found, match on URI
            HashMap urimap = (HashMap) value;
            if (urimap.get(uri) != null) {
              vctx.addError("Duplicate names are not " + "allowed in unordered collection", comp);
            } else {
              urimap.put(uri, comp);
            }

          } else {

            // duplicate name, check URI
            IComponent match = (IComponent) value;
            if ((uri == null && match.getUri() == null)
                || (uri != null && uri.equals(match.getUri()))) {
              vctx.addError("Duplicate names are not " + "allowed in unordered collection", comp);
            } else {

              // multiple namespaces for same name, use map
              HashMap urimap = new HashMap();
              urimap.put(uri, comp);
              String muri = match.getUri();
              if (muri == null) {
                muri = "";
              }
              urimap.put(muri, match);
              namemap.put(name, urimap);
            }
          }
        }
        if (vctx.isOutBinding()) {

          // just accumulate lists of each type in this loop
          String type = comp.getType().getName();
          Object value = typemap.get(type);
          if (value == null) {
            typemap.put(type, comp);
          } else if (value instanceof ArrayList) {
            ArrayList types = (ArrayList) value;
            types.add(comp);
          } else {
            ArrayList types = new ArrayList();
            types.add(value);
            types.add(comp);
            typemap.put(type, types);
          }
        }
      }
    }

    // check for duplicate type usage in output binding
    for (Iterator iter = typemap.values().iterator(); iter.hasNext(); ) {
      Object value = iter.next();
      if (value instanceof ArrayList) {

        // multiple instances of type, make sure we can distinguish
        ArrayList types = (ArrayList) value;
        for (int i = 0; i < types.size(); i++) {
          Object child = types.get(i);
          if (child instanceof ValueElement) {
            ValueElement vel = (ValueElement) child;
            if (vel.getTest() == null) {
              vctx.addError(
                  "test-method needed for "
                      + "multiple instances of same type in "
                      + "unordered collection",
                  vel);
            }
          } else if (child instanceof StructureElementBase) {
            StructureElementBase sel = (StructureElementBase) child;
            if (sel.getTest() == null) {
              vctx.addError(
                  "test-method needed for "
                      + "multiple instances of same type in "
                      + "unordered collection",
                  sel);
            }
          }
        }
      }
    }
  }
  /* (non-Javadoc)
   * @see org.jibx.binding.model.ElementBase#prevalidate(org.jibx.binding.model.ValidationContext)
   */
  public void prevalidate(ValidationContext vctx) {

    // first process attributes and check for errors
    super.prevalidate(vctx);
    if (!vctx.isSkipped(this)) {

      // check for ignored attributes
      if (isAllowRepeats()) {
        vctx.addWarning("'allow-repeats' attribute ignored on collection");
      }
      if (isChoice()) {
        vctx.addWarning("'choice' attribute ignored on collection");
      }

      // get the actual collection type and item type
      IClass clas = getType();
      if (clas == null) {
        clas = vctx.getContextObject().getObjectType();
      }
      String tname = m_itemTypeName;
      if (tname == null) {
        String ctype = clas.getName();
        if (ctype.endsWith("[]")) {
          tname = ctype.substring(0, ctype.length() - 2);
        } else {
          tname = "java.lang.Object";
        }
      }
      m_itemTypeClass = vctx.getClassInfo(tname);
      if (m_itemTypeClass == null) {
        vctx.addFatal("Can't find class " + tname);
      }

      // handle input and output bindings separately
      if (vctx.isInBinding()) {

        // check store techniques
        String sname = m_storeMethodName;
        String aname = m_addMethodName;
        if (sname != null && aname != null) {
          vctx.addWarning("Both store-method and add-method " + "supplied; using add-method");
          sname = null;
        }

        // set defaults based on collection type if needed
        if (sname == null && aname == null) {
          if (clas.isSuperclass("java.util.ArrayList")
              || clas.isSuperclass("java.util.Vector")
              || clas.isImplements("Ljava/util/Collection;")) {
            aname = "add";
          } else if (!clas.getName().endsWith("[]")) {
            vctx.addError("Need store-method or add-method for " + "input binding");
          }
        }

        // find the actual method information
        if (sname != null) {
          m_storeMethodItem = clas.getBestMethod(sname, null, new String[] {"int", tname});
          if (m_storeMethodItem == null) {
            vctx.addError("store-method " + sname + " not found in class " + clas.getName());
          }
        }
        if (aname != null) {
          m_addMethodItem = clas.getBestMethod(aname, null, new String[] {tname});
          if (m_addMethodItem == null) {
            vctx.addError("add-method " + aname + " not found in class " + clas.getName());
          }
        }
      }
      if (vctx.isOutBinding()) {

        // precheck load techniques
        String lname = m_loadMethodName;
        String sname = m_sizeMethodName;
        String iname = m_iterMethodName;
        if (lname == null) {
          if (sname != null) {
            vctx.addWarning("size-method requires load-method; " + "ignoring supplied size-method");
            sname = null;
          }
        } else {
          if (sname == null) {
            vctx.addWarning("load-method requires " + "size-method; ignoring supplied load-method");
            lname = null;
          } else {
            if (iname != null) {
              vctx.addWarning("Both load-method and " + "iter-method supplied; using load-method");
              iname = null;
            }
          }
        }

        // set defaults based on collection type if needed
        if (lname == null && iname == null) {
          if (clas.isSuperclass("java.util.ArrayList") || clas.isSuperclass("java.util.Vector")) {
            lname = "get";
            sname = "size";
          } else if (clas.isImplements("Ljava/util/Collection;")) {
            iname = "iterator";
          }
        }

        // postcheck load techniques with defaults set
        if (lname == null) {
          if (iname == null && !clas.getName().endsWith("[]")) {
            vctx.addError(
                "Need load-method and size-method, or " + "iter-method, for output binding");
          }
        } else {
          if (sname == null && iname == null) {
            vctx.addError(
                "Need load-method and size-method," + " or iter-method, for output binding");
          }
        }

        // find the actual method information
        if (lname != null) {
          m_loadMethodItem = clas.getBestMethod(lname, tname, new String[] {"int"});
          if (m_loadMethodItem == null) {
            vctx.addError("load-method " + lname + " not found in class " + clas.getName());
          }
        }
        if (iname != null) {
          m_iterMethodItem = clas.getBestMethod(iname, "java.util.Iterator", new String[0]);
          if (m_iterMethodItem == null) {
            vctx.addError("iter-method " + iname + " not found in class " + clas.getName());
          }
        }
      }
    }
  }