/** * Copyright (C) 2013-2016 The Rythm Engine project * for LICENSE and other details see: * https://github.com/rythmengine/rythmengine */ package org.rythmengine.extension; import org.rythmengine.utils.Escape; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Specify a language (e.g. JavaScript) or a format (e.g. csv). The information could be used by * Rythm to support {@link org.rythmengine.conf.RythmConfigurationKey#FEATURE_NATURAL_TEMPLATE_ENABLED * natural template feature} and * {@link org.rythmengine.conf.RythmConfigurationKey#FEATURE_SMART_ESCAPE_ENABLED smart escape feature} */ public interface ICodeType { /** * Return comment start. E.g. for HTML, it should be <code><!--</code> * * @return comment start */ String commentStart(); /** * Return comment end. For HTML it should be <code>--></code> * * @return comment end */ String commentEnd(); /** * Return escape scheme * * @return escape */ Escape escape(); /** * Some type could be embedded into another. E.g. JS and CSS could be * embedded into HTML. This method returns a regex string the direct the start * of the embedded type. * <p/> * <p>Note the regex string must support group and the {@link java.util.regex.Matcher#group(int) group 1} * must be the captured block start. For example, JS block start is <script> or * <script type="..."...>, then the <code>blockStart</code> method of JS type should be * <code>(\<\s*script\s*.*?\<).*</code></p> * * @return block start */ String blockStart(); /** * Return a regex string indicate an end of a type block * * @return block end * @see #blockStart() for regex requirement */ String blockEnd(); /** * Return true if this file type impl allow another * type be embedded inside. e.g. HTML should return true * for this method because it allows JS and CSS * be embedded inside * * @return true if this type allows embedded type */ boolean allowInternalTypeBlock(); /** * Return a set of other types that could embed this * type impl. For example, JS should return a Set * contains an HTML impl. If no other type is allowed * to embed this type, then an empty set shall * be returned * * @return true if this type allows external type */ Set<ICodeType> allowedExternalTypes(); /** * Set the parent type to the embedded type * * @param parent */ void setParent(ICodeType parent); /** * Return parent type or null if there is no parent * set on it * * @return parent type */ ICodeType getParent(); /** * Return a string that could be write into * the target java source code to create an instance * of this type * * @return the java code */ String newInstanceStr(); /** * Return recommended resource name suffix, e.g. ".html" etc * * @return proposed resource name suffix */ String resourceNameSuffix(); public static class DefImpl implements ICodeType, Cloneable { public static final DefImpl RAW = new DefImpl("RAW", null, null, Escape.RAW, ""); public static final DefImpl HTML = new DefImpl("HTML", "<!--", "-->", Escape.XML, ".html") { @Override public boolean allowInternalTypeBlock() { // HTML allow CSS and JS inside return true; } @Override public String toString() { return "HTML"; } }; public static final DefImpl XML = new DefImpl("XML", "<!--", "-->", Escape.XML, ".xml"); public static final DefImpl JS = new DefImpl("JS", "/*", "*/", Escape.JS, "(<\\s*script[^<>]*?>).*", "(\\<\\/\\s*script\\s*\\>).*", ".js") { @Override public Set<ICodeType> allowedExternalTypes() { Set<ICodeType> set = new HashSet<ICodeType>(); set.add(HTML); return set; } }; public static final DefImpl CSS = new DefImpl("CSS", "/*", "*/", Escape.JS, "(<\\s*style[^<>]*?>).*", "(\\<\\/\\s*style\\s*\\>).*", ".css") { @Override public Set<ICodeType> allowedExternalTypes() { Set<ICodeType> set = new HashSet<ICodeType>(); set.add(HTML); return set; } }; public static final DefImpl JSON = new DefImpl("JSON", null, null, Escape.JSON, ".json"); public static final DefImpl CSV = new DefImpl("CSV", null, null, Escape.CSV, ".csv"); private final String id; private final String commentStart; private final String commentEnd; private final Escape escape; private final String blockStart; private final String blockEnd; private ICodeType parent; private final String suffix; protected DefImpl(String id, String commentStart, String commentEnd, Escape escape, String suffix) { this(id, commentStart, commentEnd, escape, null, null, suffix); } protected DefImpl(String id, String commentStart, String commentEnd, Escape escape, String blockStart, String blockEnd, String suffix) { this.id = id; this.commentEnd = commentEnd; this.commentStart = commentStart; this.escape = escape; this.blockEnd = blockEnd; this.blockStart = blockStart; this.suffix = suffix; } @Override public String newInstanceStr() { StringBuilder sb = new StringBuilder(); String clsName = ICodeType.class.getName(); sb.append("(").append(clsName).append(")").append(clsName).append(".DefImpl.").append(this.id).append(".clone()"); return sb.toString(); } @Override public String commentStart() { return commentStart; } @Override public String commentEnd() { return commentEnd; } @Override public Escape escape() { return escape; } @Override public String blockStart() { return blockStart; } @Override public String blockEnd() { return blockEnd; } @Override public boolean allowInternalTypeBlock() { return false; } @Override public void setParent(ICodeType parent) { this.parent = parent; } @Override public ICodeType getParent() { return parent; } @Override public Set<ICodeType> allowedExternalTypes() { return Collections.emptySet(); } @Override public String resourceNameSuffix() { return suffix; } @Override public String toString() { return newInstanceStr(); } @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } } }