/* TODO:
 * In future, when we have a separate
 * .par file for EJB 3.0 entities, this class will stop extending {@link
 * com.sun.jdo.spi.persistence.support.ejb.ejbc.JDOCodeGenerator} and
 * lots of if checks that we have in this class will vanish.
 * Since we will have two CodeGens, one for pre 3.0 entities and one for
 * 3.0 entities, we should also rename these classes.
 * Enhancer for 3.0 is supposed to be called from this class in addition
 * to model mapping.
 */
public class JDOCodeGenerator
    extends com.sun.jdo.spi.persistence.support.ejb.ejbc.JDOCodeGenerator {

  private static final I18NHelper i18NHelper = I18NHelper.getInstance(LogHelperCMP.class);

  private static final Logger logger = LogHelperCMP.getLogger();

  private EjbBundleDescriptor ejbBundleDescriptor;

  private PersistenceJarLoader pjl;

  /**
   * need a public no arg constructor as this is used in {@link Class#newInstance()} by {@link
   * com.sun.ejb.codegen.CmpCompiler}.
   */
  public JDOCodeGenerator() {}

  /** {@inheritDoc} */
  @Override
  public void init(
      EjbBundleDescriptor ejbBundleDescriptor, EjbcContext ejbcContext, String bundlePathName)
      throws GeneratorException {
    logger.info(
        i18NHelper.msg(
            "MSG_JDOCodeGeneratorInit", // NOI18N
            JDOCodeGeneratorHelper.getModuleName(ejbBundleDescriptor)));

    this.ejbBundleDescriptor = ejbBundleDescriptor;
    if (ejbBundleDescriptor.containsCMPEntity()) {
      // do the pre EJB 3.0 related stuff here.
      super.init(ejbBundleDescriptor, ejbcContext, bundlePathName);
    }

    if (ejbBundleDescriptor.containsPersistenceEntity()) {
      // now do the EJB 3.0 specific task
      pjl = new PersistenceJarLoader();
      pjl.load(ejbBundleDescriptor);
    }
  }

  /** {@inheritDoc} */
  @Override
  public Collection cleanup() throws GeneratorException {
    logger.info(
        i18NHelper.msg(
            "MSG_JDOCodeGeneratorCleanup", // NOI18N
            JDOCodeGeneratorHelper.getModuleName(ejbBundleDescriptor)));

    Collection result = null;
    if (ejbBundleDescriptor.containsCMPEntity()) {
      // do the pre EJB 3.0 related stuff here.
      result = super.cleanup();
    }
    if (ejbBundleDescriptor.containsPersistenceEntity()) {
      // now do the EJB 3.0 specific task
      pjl.unload();
    }
    // we have nothing to return for 3.0 beans until we integrate
    // enhancer with deployment. So as per the contract,
    // we return an empty collection and not a null collection if
    // we have only 3.0 beans.
    return result == null ? Collections.emptyList() : result;
  }
}
/**
 * The class reads XML documents according to specified DTD and translates all related events into
 * JDOHandler events.
 *
 * <p>Usage sample:
 *
 * <pre>
 *    JDOParser parser = new JDOParser(...);
 *    parser.parse(new InputSource("..."));
 * </pre>
 *
 * <p><b>Warning:</b> the class is machine generated. DO NOT MODIFY
 */
public class JDOParser implements ContentHandler {

  /** I18N support. */
  private static final I18NHelper msg =
      I18NHelper.getInstance(
          "com.sun.org.apache.jdo.impl.model.jdo.Bundle",
          JDOParser.class.getClassLoader()); // NOI18N

  private StringBuffer buffer;

  private JDOHandler handler;

  private java.util.Stack context;

  public JDOParser(final JDOHandler handler) {
    this.handler = handler;
    buffer = new StringBuffer(111);
    context = new java.util.Stack();
  }

  public void setDocumentLocator(Locator locator) {}

  public void startDocument() throws SAXException {}

  public void endDocument() throws SAXException {}

  public void startElement(String ns, String name, String qname, Attributes attrs)
      throws SAXException {
    dispatch(true);
    context.push(new Object[] {qname, new org.xml.sax.helpers.AttributesImpl(attrs)});

    if ("package".equals(name)) { // NOI18N
      handler.start_package(attrs);
    } else if ("jdo".equals(name)) { // NOI18N
      handler.start_jdo(attrs);
    } else if ("class".equals(name)) { // NOI18N
      handler.start_class(attrs);
    } else if ("map".equals(name)) { // NOI18N
      handler.start_map(attrs);
    } else if ("field".equals(name)) { // NOI18N
      handler.start_field(attrs);
    } else if ("collection".equals(name)) { // NOI18N
      handler.start_collection(attrs);
    } else if ("extension".equals(name)) { // NOI18N
      handler.start_extension(attrs);
    } else if ("array".equals(name)) { // NOI18N
      handler.start_array(attrs);
    }
  }

  public void endElement(String ns, String name, String qname) throws SAXException {
    dispatch(false);
    context.pop();
    if ("package".equals(name)) { // NOI18N
      handler.end_package();
    } else if ("jdo".equals(name)) { // NOI18N
      handler.end_jdo();
    } else if ("class".equals(name)) { // NOI18N
      handler.end_class();
    } else if ("map".equals(name)) { // NOI18N
      handler.end_map();
    } else if ("field".equals(name)) { // NOI18N
      handler.end_field();
    } else if ("collection".equals(name)) { // NOI18N
      handler.end_collection();
    } else if ("extension".equals(name)) { // NOI18N
      handler.end_extension();
    } else if ("array".equals(name)) { // NOI18N
      handler.end_array();
    }
  }

  public void characters(char[] chars, int start, int len) throws SAXException {
    buffer.append(chars, start, len);
  }

  public void ignorableWhitespace(char[] chars, int start, int len) throws SAXException {}

  public void processingInstruction(String target, String data) throws SAXException {}

  public void startPrefixMapping(final String prefix, final String uri) throws SAXException {}

  public void endPrefixMapping(final String prefix) throws SAXException {}

  public void skippedEntity(String name) throws SAXException {}

  private void dispatch(final boolean fireOnlyIfMixed) throws SAXException {
    if (fireOnlyIfMixed && buffer.length() == 0) return; // skip it

    Object[] ctx = (Object[]) context.peek();
    String here = (String) ctx[0];
    Attributes attrs = (Attributes) ctx[1];
    buffer.delete(0, buffer.length());
  }

  /**
   * The recognizer entry method taking an InputSource.
   *
   * @param input InputSource to be parsed.
   * @throws java.io.IOException on I/O error.
   * @throws SAXException propagated exception thrown by a DocumentHandler.
   * @throws javax.xml.parsers.ParserConfigurationException a parser satisfining requested
   *     configuration can not be created.
   * @throws javax.xml.parsers.FactoryConfigurationError if the implementation can not be
   *     instantiated.
   */
  public void parse(final InputSource input)
      throws SAXException, ParserConfigurationException, IOException {
    parse(input, this);
  }

  /**
   * The recognizer entry method taking a URL.
   *
   * @param url URL source to be parsed.
   * @throws java.io.IOException on I/O error.
   * @throws SAXException propagated exception thrown by a DocumentHandler.
   * @throws javax.xml.parsers.ParserConfigurationException a parser satisfining requested
   *     configuration can not be created.
   * @throws javax.xml.parsers.FactoryConfigurationError if the implementation can not be
   *     instantiated.
   */
  public void parse(final java.net.URL url)
      throws SAXException, ParserConfigurationException, IOException {
    parse(new InputSource(url.toExternalForm()), this);
  }

  /**
   * The recognizer entry method taking an Inputsource.
   *
   * @param input InputSource to be parsed.
   * @throws java.io.IOException on I/O error.
   * @throws SAXException propagated exception thrown by a DocumentHandler.
   * @throws javax.xml.parsers.ParserConfigurationException a parser satisfining requested
   *     configuration can not be created.
   * @throws javax.xml.parsers.FactoryConfigurationError if the implementation can not be
   *     instantiated.
   */
  public static void parse(final InputSource input, final JDOHandler handler)
      throws SAXException, ParserConfigurationException, IOException {
    parse(input, new JDOParser(handler));
  }

  /**
   * The recognizer entry method taking a URL.
   *
   * @param url URL source to be parsed.
   * @throws java.io.IOException on I/O error.
   * @throws SAXException propagated exception thrown by a DocumentHandler.
   * @throws javax.xml.parsers.ParserConfigurationException a parser satisfining requested
   *     configuration can not be created.
   * @throws javax.xml.parsers.FactoryConfigurationError if the implementation can not be
   *     instantiated.
   */
  public static void parse(final java.net.URL url, final JDOHandler handler)
      throws SAXException, ParserConfigurationException, IOException {
    parse(new InputSource(url.toExternalForm()), handler);
  }

  private static void parse(final InputSource input, final JDOParser recognizer)
      throws SAXException, ParserConfigurationException, IOException {
    SAXParserFactory factory = SAXParserFactory.newInstance();
    factory.setValidating(true);
    factory.setNamespaceAware(true);
    XMLReader parser = factory.newSAXParser().getXMLReader();
    parser.setEntityResolver(new JDOEntityResolver());
    parser.setContentHandler(recognizer);
    parser.setErrorHandler(recognizer.getDefaultErrorHandler());
    parser.parse(input);
  }

  private ErrorHandler getDefaultErrorHandler() {
    return new ErrorHandler() {
      public void error(SAXParseException ex) throws SAXException {
        if (context.isEmpty()) System.err.println("Missing DOCTYPE."); // NOI18N
        throw ex;
      }

      public void fatalError(SAXParseException ex) throws SAXException {
        throw ex;
      }

      public void warning(SAXParseException ex) throws SAXException {
        // ignore
      }
    };
  }

  /** Implementation of EntityResolver interface to check the jdo.dtd location */
  private static class JDOEntityResolver implements EntityResolver {
    private static final String RECOGNIZED_PUBLIC_ID =
        "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 1.0//EN"; // NOI18N
    private static final String RECOGNIZED_SYSTEM_ID = "file:/javax/jdo/jdo.dtd"; // NOI18N

    public InputSource resolveEntity(String publicId, String systemId)
        throws SAXException, IOException {
      // check for recognized ids
      if (((publicId != null) && RECOGNIZED_PUBLIC_ID.equals(publicId))
          || ((publicId == null) && (systemId != null) && RECOGNIZED_SYSTEM_ID.equals(systemId))) {
        // Substitute the dtd with the one from
        // com.sun.persistence.support.jdo.dtd, but only if the
        // publicId is equal to RECOGNIZED_PUBLIC_ID or there is no
        // publicID and the systemID is equal to RECOGNIZED_SYSTEM_ID.
        InputStream stream =
            (InputStream)
                AccessController.doPrivileged(
                    new PrivilegedAction() {
                      public Object run() {
                        return getClass()
                            .getClassLoader()
                            .getResourceAsStream("com/sun/persistence/support/jdo.dtd"); // NOI18N
                      }
                    });
        if (stream == null) {
          throw new RuntimeException(
              msg.msg(
                  "EXC_MissingJDODTD", // NOI18N
                  publicId,
                  systemId));
        }
        return new InputSource(new InputStreamReader(stream));
      }
      return null;
    }
  }
}