/**
* Copyright 2010 Bing Ran<bing_ran@hotmail.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 cn.bran.japid.compiler;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.regex.Pattern;
import cn.bran.japid.classmeta.AbstractTemplateClassMetaData;
import cn.bran.japid.template.JapidTemplate;
import cn.bran.japid.util.DirUtil;
/**
* compile html based template to java files
*
* The facade to all the compiler suite and configurations.
*
* @author Bing Ran<bing_ran@hotmail.com>
*
*/
public class JapidTemplateTransformer {
private static final String HTML = ".html";
private String sourceFolder;
private String targetFolder;
private boolean usePlay = true;
// private MessageProvider messageProvider;
// private UrlMapper urlMapper;
/**
* @param sourceFolder
* the folder containing the template source tree. It's the same
* concept as in eclipse. The root package for Java artifacts are
* the direct children of this
* @param targetFolder
* the "source folder", in Eclipse term, that the generated Java
* artifacts will be placed. If it is null, the Java files will
* be placed in the sourceFolder.
*/
public JapidTemplateTransformer(String sourceFolder, String targetFolder) {
super();
this.sourceFolder = sourceFolder;
// this.messageProvider = messageProvider;
// this.urlMapper = urlMapper;
this.targetFolder = targetFolder;
//
// BranTemplateBase.messageProvider = messageProvider;
// BranTemplateBase.urlMapper = urlMapper;
}
/**
*
* @param importLine
* add an import to all the files generated. For examples:
* "my.package.*", "my.package.MyClass"
*/
public void addImportLine(String importLine) {
AbstractTemplateClassMetaData.addImportLineGlobal(importLine);
}
/**
* effectively as in Java: "import static my.Tools.*;" if Tools.class is the
* parameter.
*
* @param class1
*/
public void addImportStatic(Class<?> class1) {
AbstractTemplateClassMetaData.addImportStatic(class1);
}
static Pattern doLayoutTag = Pattern.compile(".*\\#\\{\\s*doLayout\\s*.*");
static Pattern doLayoutDirective = Pattern.compile(".*`doLayout");
static Pattern doLayoutDirective2 = Pattern.compile(".*`doLayout\\s?`.*");
static Pattern getTag= Pattern.compile(".*\\#\\{\\s*get\\s*.*");
static Pattern getDirective = Pattern.compile(".*`get\\s+\\w+\\s*");
public static boolean looksLikeLayout(String src) {
String[] split = src.split("[\r\n]");
for (String line : split) {
line = line.trim();
if (doLayoutTag.matcher(line).matches()
|| doLayoutDirective.matcher(line).matches()
|| doLayoutDirective2.matcher(line).matches()
|| getTag.matcher(line).matches()
|| getDirective.matcher(line).matches()) {
return true;
}
}
return false;
}
/**
*
* @param fileName
* the relative path of the template file from the source folder,
* e.g., "my/path/mytemplate.html", which maps to
* my.path.mytemplate.java
* @return
* @throws Exception
*/
public File generate(String fileName) throws Exception {
String realSrcFile = sourceFolder == null ? fileName : sourceFolder + "/" + fileName;
String src = readFileAsString(realSrcFile);
JapidTemplate temp = new JapidTemplate(fileName, src);
JapidAbstractCompiler c = null;
// TODO: more robust way of determine layout file or view file
if (looksLikeLayout(src)) {
c = new JapidLayoutCompiler();
} else {
// regular template and tag are the same thing
c = new JapidTemplateCompiler();
}
c.setUseWithPlay(usePlay);
c.compile(temp);
String jsrc = temp.javaSource;
// String fileNameRoot = fileName;
// if (fileName.endsWith(HTML)) {
// fileNameRoot = fileName.substring(0, fileName.indexOf(HTML));
// }
String fileNameRoot = DirUtil.mapSrcToJava(fileName);
String target = targetFolder == null ? sourceFolder : targetFolder;
String realTargetFile = target == null ? fileNameRoot : target + "/" + fileNameRoot;
File f = new File(realTargetFile);
if (!f.exists()) {
String parent = f.getParent();
new File(parent).mkdirs();
}
BufferedOutputStream bf = new BufferedOutputStream(new FileOutputStream(f));
bf.write(jsrc.getBytes("UTF-8"));
bf.close();
return f;
}
// this method is entirely safe ???
private String readFileAsString(String filePath) throws Exception {
// let're remove dependency on commons IO
// return IOUtils.toString(new FileInputStream(filePath), "UTF-8");
byte[] buffer = new byte[(int) new File(filePath).length()];
BufferedInputStream f = new BufferedInputStream(new FileInputStream(filePath));
// not sure if this is always safe assume it'll read all bytes in
f.read(buffer);
f.close();
return new String(buffer, "UTF-8");
}
/**
* a utility method. Should be somewhere else.
*
* @param srcDir
* @param cf
* @throws IOException
*/
public static String getRelativePath(File child, File parent) throws IOException {
String curPath = parent.getCanonicalPath();
String childPath = child.getCanonicalPath();
assert (childPath.startsWith(curPath));
String srcRelative = childPath.substring(curPath.length());
if (srcRelative.startsWith(File.separator)) {
srcRelative = srcRelative.substring(File.separator.length());
}
return srcRelative;
}
/**
* transform a source template to Java
*
* @param file
* the source file that must be a descendant of the source folder.
* @return
* @throws Exception
*/
public File generate(File file) throws Exception {
String rela = getRelativePath(file, new File("."));
return generate(rela);
}
/**
* add class level annotation for whatever purpose
*
* @param anno
*/
public void addAnnotation(Class<? extends Annotation> anno) {
AbstractTemplateClassMetaData.addAnnotation(anno);
// typeAnnotations.add(anno);
}
public void usePlay(boolean b) {
this.usePlay = b;
}
// List<Class<? extends Annotation>> typeAnnotations = new ArrayList<Class<?
// extends Annotation>>();
}