/*
* 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.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
import javax.xml.transform.TransformerException;
import com.spoledge.util.xml.XMLValidator;
import com.spoledge.util.xml.XMLValidatorInstance;
import com.spoledge.util.xslt.ResourceURIResolver;
import com.spoledge.util.xslt.TemplatesCache;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This is the main Generator class.
* This class is thread safe.
*
* <pre>
* Generator g = new Generato( Generator.Target.ORACLE );
* g.generate( xml, new FileOutput());
* </pre>
*/
public class Generator {
/**
* The global resources located in the JAR.
*/
public static final String RESOURCES = "/resources";
/**
* The global XSD resources located in the JAR.
*/
public static final String XSD_RESOURCES = RESOURCES + "/xsd";
/**
* The global XSL resources located in the JAR.
*/
public static final String XSL_RESOURCES = RESOURCES + "/xsl";
/**
* The global XSL resources located in the JAR.
*/
public static final String JAVA_RESOURCES = RESOURCES + "/java";
/**
* If no specific cache is provided, then all Generators
* share the same global cache.
*/
private static TemplatesCache globalTemplatesCache;
/**
* Precomputed set of AUDAO Java resource names for each Target.
* This structure is lazy.
*/
private static HashMap<Target, String[]> javaResourceKeys = new HashMap<Target, String[]>();
/**
* The AuDAO XML precompiled validator.
*/
private static XMLValidator xmlValidator;
////////////////////////////////////////////////////////////////////////////
// Attributes
////////////////////////////////////////////////////////////////////////////
private Target target;
private TemplatesCache templatesCache;
private ResourceURIResolver resourceURIResolver = new ResourceURIResolver( XSL_RESOURCES );
private String[] resourceKeys = new String[ 10 ];
private boolean[] resourcesEnabled = new boolean[ 10 ];
private boolean isDebugEnabled = false;
private Log log = LogFactory.getLog(getClass());
////////////////////////////////////////////////////////////////////////////
// Constructors
////////////////////////////////////////////////////////////////////////////
public Generator( Target target ) {
if (target == null) throw new NullPointerException("Target cannost be null");
this.target = target;
}
////////////////////////////////////////////////////////////////////////////
// Public
////////////////////////////////////////////////////////////////////////////
/**
* Returns the target of this generator.
*/
public final Target getTarget() {
return target;
}
/**
* Enables/disables generating type of resource.
*/
public void setResourceEnabled( ResourceType type, boolean enabled ) {
resourcesEnabled[ type.ordinal()] = enabled;
}
/**
* Enables/disables generating all types of resources.
* NOTE: this method does NOT force generating inapplicable resources
* for given Target (e.g. DTO_IMPL for "mysql")
*/
public void setAllResourcesEnabled( boolean enabled ) {
for (int i=0; i < resourcesEnabled.length; i++) {
resourcesEnabled[ i ] = enabled;
}
}
/**
* Returns true if a debug mode is enabled.
*/
public boolean getIsDebugEnabled() {
return isDebugEnabled;
}
/**
* Sets the debug mode flag.
*/
public void setIsDebugEnabled( boolean enabled ) {
this.isDebugEnabled = enabled;;
}
/**
* Returns the resource key.
* It detects whether the implementation file is present
* (e.g. "dao/mysql/dto.xsl") and returns that or returns
* general version ("dao/dto.xsl").
*
* @return the resource key or null if not applicable (e.g. SQL_CREATE for GAE)
*/
public String getResourceKey( ResourceType type ) {
int index = type.ordinal();
String ret = resourceKeys[ index ];
if (ret == null) {
ret = getXslName( type, true );
if (getClass().getResource( ret ) == null) {
ret = type.getIsOptional() ? "" : getXslName( type, false );
}
resourceKeys[ index ] = ret;
}
return ret.length() != 0 ? ret : null;
}
/**
* Validates source XML.
*/
public void validate( String xml ) throws Exception {
validate( new StringReader( xml ));
}
/**
* Validates source XML.
*/
public void validate( Reader reader ) throws Exception {
XMLValidator xv = getXMLValidator();
XMLValidatorInstance xvi = xv.validate( reader );
if (xvi.isInvalid()) throw xvi.getParseException();
}
/**
* Generates the result files.
*/
public void generate( String pkgName, String xml, Output output ) throws IOException, GeneratorException {
generate( pkgName, new StringReader( xml ), output );
}
/**
* Generates the result files.
*/
public void generate( String pkgName, Reader reader, Output output ) throws IOException, GeneratorException {
GeneratorFlow gf = new GeneratorFlow( this, pkgName, reader, output );
boolean generated = false;
try {
for (ResourceType type : ResourceType.values()) {
if (resourcesEnabled[ type.ordinal()]) {
gf.generate( type );
generated = true;
}
}
if (gf.hasExceptions()) throw new GeneratorException( gf );
}
catch (TransformerException e) {
if (gf.hasExceptions()) throw new GeneratorException( gf );
else throw new GeneratorException( e );
}
if (gf.hasExceptions()) throw new GeneratorException( gf );
if (!generated) {
log.warn("generate(): nothing generated since no resource type enabled in the Generator");
}
}
/**
* Sets a templates cache to use.
* By default Generator uses default MemoryCache same for all instances.
*/
public void setTemplatesCache( TemplatesCache tc ) {
tc.getTransformerFactory().setURIResolver( resourceURIResolver );
this.templatesCache = tc;
}
/**
* Returns the cache of XSL templates.
*/
public synchronized TemplatesCache getTemplatesCache() {
if (templatesCache == null) {
synchronized (Generator.class) {
if (globalTemplatesCache == null) {
globalTemplatesCache = new TemplatesCache();
globalTemplatesCache.getTransformerFactory().setURIResolver( resourceURIResolver );
}
}
templatesCache = globalTemplatesCache;
}
return templatesCache;
}
/**
* Returns the ResourceURIResolver.
*/
public ResourceURIResolver getResourceURIResolver() {
return resourceURIResolver;
}
/**
* Returns the resource keys of the AUDAO Java source files.
*/
public String[] getJavaResourceKeys() {
synchronized (javaResourceKeys) {
String[] ret = javaResourceKeys.get( target );
if (ret == null) {
ret = createJavaResourceKeys();
javaResourceKeys.put( target, ret );
}
return ret;
}
}
////////////////////////////////////////////////////////////////////////////
// Private
////////////////////////////////////////////////////////////////////////////
private String getXslName( ResourceType type, boolean isSpec ) {
return XSL_RESOURCES + '/' + type.getDir() + '/'
+ (isSpec ? getTarget().getIdentifier() + '/' : "") + type.getName();
}
private String[] createJavaResourceKeys() {
ArrayList<String> list = new ArrayList<String>();
Properties props = new Properties();
try {
props.load( getClass().getResourceAsStream( JAVA_RESOURCES + "/sources.properties" ));
}
catch (IOException e) {
log.error("createJavaResourceKeys()", e);
}
// NOTE: JDK 1.5 compatibility - we cannot use stringPropertyNames():
// for (String key : props.stringPropertyNames()) {
for (Enumeration<?> en = props.propertyNames(); en.hasMoreElements();) {
String key = (String) en.nextElement();
String[] vals = props.getProperty( key ).split("\\|");
if (vals.length == 1) {
list.add( vals[0] );
}
else {
String tname = target.getIdentifier().toLowerCase();
for (int i=1; i < vals.length; i++) {
if (tname.equals( vals[i] )) {
list.add( vals[0] );
break;
}
}
}
}
String[] ret = new String[ list.size()];
return list.toArray( ret );
}
private static XMLValidator getXMLValidator() {
synchronized (Generator.class) {
if (xmlValidator == null) {
try {
xmlValidator = new XMLValidator( Generator.class.getResource( XSD_RESOURCES + "/audao.xsd" ));
}
catch (Exception e) {
LogFactory.getLog( Generator.class ).error("getXMLValidator(): ", e);
}
}
return xmlValidator;
}
}
}