package com.skaringa.javaxml.handler.sax;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import com.skaringa.javaxml.SerializerException;
import com.skaringa.javaxml.handler.AttrImpl;
import com.skaringa.javaxml.handler.DocumentOutputHandlerInterface;
import com.skaringa.javaxml.serializers.ComponentSerializer;
import com.skaringa.javaxml.serializers.SerializerRegistry;
/**
 * A implementation of the SAX2 XMLReader interface.
 * This class is able to parse serializable objects and write their type
 * definition as an XML schema definition (XSD).
 */
public final class ObjectXSDReader extends AbstractXMLReader {
  private Collection _dependendClasses = new Vector();
  /**
   * @see AbstractXMLReader#parseObject(Object, Class, DocumentOutputHandlerInterface)
   */
  public void parseObject(
    Object obj,
    Class type,
    DocumentOutputHandlerInterface output)
    throws SerializerException {
    try {
      type = (Class) obj;
    }
    catch (ClassCastException e) {
      throw new SerializerException(
        "wrong context: obj needs to be of type Class, but is: "
          + obj.getClass().getName());
    }
    SerializerRegistry reg = SerializerRegistry.getInstance();
    ComponentSerializer ser = reg.getSerializer(type);
    output.startDocument();
    output.startElement("xsd:schema");
    AttrImpl attr = new AttrImpl();
    attr.addAttribute("name", ser.getXMLTypeName());
    attr.addAttribute("type", ser.getXMLTypeName());
    attr.addAttribute("nillable", "true");
    output.startElement("xsd:element", attr);
    output.endElement("xsd:element");
    // get and document all used classes
    _dependendClasses.add(type);
    Set usedClasses = new HashSet();
    Iterator it = _dependendClasses.iterator();
    while (it.hasNext()) {
      Class aClass = (Class) it.next();
      ser = reg.getSerializer(aClass);
      ser.addUsedClasses(aClass, usedClasses);
    }
    documentUsedClasses(usedClasses, output);
    output.endElement("xsd:schema");
    output.endDocument();
  }
  /**
   * Add another class to document within the schema.
   * This is useful for generating schemas for collections,
   * because their component types are not documented in the class definition.
   * @param usedClass The class used.
   */
  public void addUsedClass(Class usedClass) {
    _dependendClasses.add(usedClass);
  }
  /**
   * Add a collection of classes to document within the schema.
   * This is useful for generating schemas for collections,
   * because their component types are not documented in the class definition.
   * @param usedClasses A collection of classes used.
   */
  public void addUsedClasses(Collection usedClasses) {
    _dependendClasses.addAll(usedClasses);
  }
  /**
   * Write the XML type definitions of the used classes.
   * @param theClasses List of classes to document.
   * @param output The output to write the definition to.
   * @throws SerializerException If something goes wrong.
   */
  private void documentUsedClasses(
    Collection theClasses,
    DocumentOutputHandlerInterface output)
    throws SerializerException {
    SerializerRegistry reg = SerializerRegistry.getInstance();
    Iterator it = theClasses.iterator();
    while (it.hasNext()) {
      Class type = (Class) it.next();
      ComponentSerializer ser = reg.getSerializer(type);
      ser.writeXMLTypeDefinition(type, getPropertyMap(), output);
    }
  }
}