/*
* Copyright 2010 Spolecne s.r.o. (www.spoledge.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.spoledge.audao.generator;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import com.spoledge.util.xslt.TemplatesCache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This is actualy the worker.
*/
public class GeneratorFlow implements ErrorListener {
private Generator generator;
private String pkgName;
private String dirName;
private Reader xmlReader;
private Output output;
private String xmlPreprocessed;
private Properties listProps;
private Set<String> tableNames;
private byte[] buffer;
private ArrayList<TransformerException> exceptions;
private ArrayList<GeneratorException.Type> exceptionTypes;
private boolean isFatalError = false;
private Log log = LogFactory.getLog(getClass());
////////////////////////////////////////////////////////////////////////////
// Constructors
////////////////////////////////////////////////////////////////////////////
public GeneratorFlow( Generator generator, String pkgName, Reader xmlReader, Output output ) {
this.generator = generator;
this.pkgName = pkgName;
this.xmlReader = xmlReader;
this.output = output;
dirName = "dao/" + pkgName.replace( '.', '/' ) + '/';
}
////////////////////////////////////////////////////////////////////////////
// Public
////////////////////////////////////////////////////////////////////////////
public void generate( ResourceType type ) throws IOException, TransformerException {
switch ( type ) {
case DTO: generateDtos(); break;
case DTO_IMPL: generateDtoImpls(); break;
case DTO_GWT_SERIALIZER: generateDtoGwtSerializers(); break;
case DAO: generateDaos(); break;
case DAO_IMPL: generateDaoImpls(); break;
case FACTORY: generateFactory(); break;
case FACTORY_IMPL: generateFactoryImpl(); break;
case SQL_CREATE: generateSqlCreate(); break;
case SQL_DROP: generateSqlDrop(); break;
case AUDAO_JAVA: generateAudaoJava(); break;
default:
log.warn("generate(): unknown resource type " + type);
}
}
public void generateDtos() throws IOException, TransformerException {
Transformer transformer = getTransformer( ResourceType.DTO );
String path = dirName + "dto/";
for (String tableName : getTableNames()) {
if (hasDto( tableName )) {
transformer.setParameter( "table_name", tableName);
transformer.transform( getSource(), output.addResult( file( path, tableName, "" )));
}
}
}
public void generateDtoGwtSerializers() throws IOException, TransformerException {
Transformer transformer = getTransformer( ResourceType.DTO_GWT_SERIALIZER );
String path = dirName + "dto/";
for (String tableName : getTableNames()) {
if (hasDto( tableName )) {
transformer.setParameter( "table_name", tableName);
transformer.transform( getSource(), output.addResult( file( path, tableName, "_CustomFieldSerializer" )));
}
}
}
public void generateDtoImpls() throws IOException, TransformerException {
Transformer transformer = getTransformer( ResourceType.DTO_IMPL );
if (transformer == null) return;
String path = dirName + "dto/" + generator.getTarget().getIdentifier() + '/';
for (String tableName : getTableNames()) {
if (hasDto( tableName )) {
transformer.setParameter( "table_name", tableName);
transformer.transform( getSource(), output.addResult( file( path, tableName, "Impl" )));
}
}
}
public void generateDaos() throws IOException, TransformerException {
Transformer transformer = getTransformer( ResourceType.DAO );
String path = dirName + "dao/";
for (String tableName : getTableNames()) {
transformer.setParameter( "table_name", tableName);
transformer.transform( getSource(), output.addResult( file( path, tableName, "Dao" )));
}
}
public void generateDaoImpls() throws IOException, TransformerException {
Transformer transformer = getTransformer( ResourceType.DAO_IMPL );
setDbType( transformer );
String path = dirName + "dao/" + generator.getTarget().getIdentifier() + '/';
for (String tableName : getTableNames()) {
transformer.setParameter( "table_name", tableName);
transformer.transform( getSource(), output.addResult( file( path, tableName, "DaoImpl" )));
}
}
public void generateFactory() throws IOException, TransformerException {
String path = dirName + "dao/DaoFactory.java";
Transformer transformer = getTransformer( ResourceType.FACTORY );
setDbType( transformer );
transformer.transform( getSource(), output.addResult( path ));
}
public void generateFactoryImpl() throws IOException, TransformerException {
String path = dirName + "dao/" + generator.getTarget().getIdentifier() + "/DaoFactoryImpl.java";
Transformer transformer = getTransformer( ResourceType.FACTORY_IMPL );
setDbType( transformer );
transformer.transform( getSource(), output.addResult( path ));
}
public void generateSqlCreate() throws IOException, TransformerException {
generateSql( ResourceType.SQL_CREATE, "sql/create-tables.sql" );
}
public void generateSqlDrop() throws IOException, TransformerException {
generateSql( ResourceType.SQL_DROP, "sql/drop-tables.sql" );
}
public void generateAudaoJava() throws IOException {
for (String resourceKey : generator.getJavaResourceKeys()) {
copy( Generator.JAVA_RESOURCES + '/' + resourceKey, "dao/" + resourceKey );
}
}
/**
* Returns true if it caught at least error or warning.
*/
public boolean hasExceptions() {
return exceptions != null;
}
/**
* Returns all caught exceptions.
* @return the array or null
*/
public ArrayList<TransformerException> getExceptions() {
return exceptions;
}
/**
* Returns all caught exception types.
* @return the array or null
*/
public ArrayList<GeneratorException.Type> getExceptionTypes() {
return exceptionTypes;
}
////////////////////////////////////////////////////////////////////////////
// ErrorListener
////////////////////////////////////////////////////////////////////////////
public void warning( TransformerException e ) throws TransformerException {
if (log.isDebugEnabled()) {
log.debug("warning(): " + e.getMessage());
}
storeException( e, GeneratorException.Type.WARNING );
}
public void error( TransformerException e ) throws TransformerException {
if (log.isDebugEnabled()) {
log.debug("error(): " + e.getMessage());
}
storeException( e, GeneratorException.Type.ERROR );
}
public void fatalError( TransformerException e ) throws TransformerException {
if (log.isDebugEnabled()) {
log.debug("fatalError(): " + e.getMessage());
}
storeException( e, GeneratorException.Type.FATAL_ERROR );
isFatalError = true;
}
////////////////////////////////////////////////////////////////////////////
// Private
////////////////////////////////////////////////////////////////////////////
private void generateSql( ResourceType type, String name ) throws IOException, TransformerException {
Transformer transformer = getTransformer( type );
if (transformer == null) return;
setDbType( transformer );
transformer.setParameter( "db_user", "__USERNAME__");
transformer.transform( getSource(), output.addResult( name ));
}
private void generateOne( ResourceType type, String name ) throws IOException, TransformerException {
Transformer transformer = getTransformer( type );
if (transformer == null) return;
transformer.transform( getSource(), output.addResult( name ));
}
private String file( String path, String tableName, String suffix ) {
String javaName = listProps.getProperty( tableName );
int index = javaName.indexOf('|');
if (index != -1) javaName = javaName.substring( 0, index );
String ret = path + javaName + suffix + ".java";
if (log.isDebugEnabled()) {
log.debug("file(): " + ret);
}
return ret;
}
private boolean hasDto( String tableName) {
String javaName = listProps.getProperty( tableName );
return javaName.indexOf("|NO-DTO") == -1;
}
private Set<String> getTableNames() throws IOException, TransformerException {
if (tableNames != null) return tableNames;
Transformer transformer = getTransformer( resourceKey( "dao/gen-list.xsl" ));
StringWriter sw = new StringWriter();
StreamSource source = new StreamSource( new StringReader( getXmlPreprocessed()));
StreamResult result = new StreamResult( sw );
transformer.transform( source, result );
listProps = new Properties();
// NOTE: JDK 1.5 compatibility - we cannot use load(Reader):
// listProps.load( new StringReader( sw.toString()));
listProps.load( new ByteArrayInputStream( sw.toString().getBytes("UTF-8")));
// NOTE: JDK 1.5 compatibility - we cannot use stringPropertyNames():
// tableNames = listProps.stringPropertyNames();
tableNames = new LinkedHashSet<String>();
for (Enumeration en = listProps.propertyNames(); en.hasMoreElements();) {
tableNames.add( (String) en.nextElement());
}
return tableNames;
}
private StreamSource getSource() throws TransformerException {
return new StreamSource( new StringReader( getXmlPreprocessed()));
}
private String getXmlPreprocessed() throws TransformerException {
if (xmlPreprocessed != null) return xmlPreprocessed;
Transformer transformer = getTransformer( resourceKey( "preprocess.xsl" ));
StringWriter sw = new StringWriter();
StreamSource source = new StreamSource( xmlReader );
StreamResult result = new StreamResult( sw );
transformer.transform( source, result );
xmlPreprocessed = sw.toString();
if (log.isDebugEnabled()) {
log.debug("getXmlPreprocessed():\n" + xmlPreprocessed);
}
if (generator.getIsDebugEnabled()) {
OutputStream os = null;
try {
os = output.addStream("preprocessed.xml");
os.write( xmlPreprocessed.getBytes( "UTF-8" ));
os.flush();
}
catch (Exception e) {
log.error("getXmlPreprocessed():", e);
}
finally {
if (os != null) try { os.close(); } catch (Exception ee) {}
}
}
return xmlPreprocessed;
}
private Transformer getTransformer( ResourceType type ) {
String resourceKey = generator.getResourceKey( type );
return resourceKey != null ? getTransformer( resourceKey ) : null;
}
private Transformer getTransformer( String resourceKey ) {
try {
TemplatesCache tc = generator.getTemplatesCache();
Templates templates = tc.getTemplates( resourceKey );
if (templates == null) {
InputStream is = getClass().getResourceAsStream( resourceKey );
templates = tc.getTemplates( resourceKey, new StreamSource( is, resourceKey ));
}
Transformer ret = templates.newTransformer();
ret.setURIResolver( generator.getResourceURIResolver()); // not auto copied from TrFactory
ret.setErrorListener( this );
ret.setParameter( "pkg_db", pkgName );
return ret;
}
catch (TransformerConfigurationException e) {
throw new RuntimeException( e );
}
}
private String resourceKey( String name ) {
return Generator.XSL_RESOURCES + '/' + name;
}
private void copy( String resourceKey, String path ) throws IOException {
InputStream is = getClass().getResourceAsStream( resourceKey );
if (is == null) throw new IOException("Cannot find resource: " + path);
OutputStream os = output.addStream( path );
if (buffer == null) buffer = new byte[ 2048 ];
int n;
while ((n = is.read( buffer, 0, buffer.length)) != -1) {
os.write( buffer, 0, n );
}
os.flush();
}
private void setDbType( Transformer transformer ) {
transformer.setParameter( "db_type", generator.getTarget().getIdentifier());
}
private void storeException( TransformerException e, GeneratorException.Type type )
throws TransformerException {
// Xalan sends the error messages twice:
if (isFatalError) return;
if (exceptions == null) {
exceptions = new ArrayList<TransformerException>();
exceptionTypes = new ArrayList<GeneratorException.Type>();
}
exceptions.add( e );
exceptionTypes.add( type );
}
}