X-Git-Url: https://code.wpia.club/?p=gigi.git;a=blobdiff_plain;f=src%2Forg%2Fcacert%2Fgigi%2Foutput%2Ftemplate%2FTemplate.java;h=15f3408d28ce41d10d55b4f9491fdaf223cb0d84;hp=bfff646f8fe833164ea30e6129275828045c7b24;hb=2e2a2b842beac5c70b53ae379e97c8a1e688e12f;hpb=ffab4ac1fae1a58919c7dde59d90c75df096512c diff --git a/src/org/cacert/gigi/output/template/Template.java b/src/org/cacert/gigi/output/template/Template.java index bfff646f..15f3408d 100644 --- a/src/org/cacert/gigi/output/template/Template.java +++ b/src/org/cacert/gigi/output/template/Template.java @@ -9,149 +9,221 @@ import java.io.PrintWriter; import java.io.Reader; import java.net.URISyntaxException; import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Collection; +import java.util.Date; import java.util.LinkedList; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.cacert.gigi.DevelLauncher; -import org.cacert.gigi.Language; -import org.cacert.gigi.output.Outputable; +import org.cacert.gigi.localisation.Language; +import org.cacert.gigi.output.DateSelector; +import org.cacert.gigi.util.DayDate; +import org.cacert.gigi.util.HTMLEncoder; +/** + * Represents a loaded template file. + */ public class Template implements Outputable { - TemplateBlock data; - - long lastLoaded; - File source; - - private static final Pattern IF_PATTERN = Pattern.compile(" ?if\\(\\$([^)]+)\\) ?\\{ ?"); - - public Template(URL u) { - try { - Reader r = new InputStreamReader(u.openStream(), "UTF-8"); - try { - if (u.getProtocol().equals("file") && DevelLauncher.DEVEL) { - source = new File(u.toURI()); - lastLoaded = source.lastModified() + 1000; - } - } catch (URISyntaxException e) { - e.printStackTrace(); - } - data = parse(r); - r.close(); - } catch (IOException e) { - throw new Error(e); - } - } - - public Template(Reader r) { - try { - data = parse(r); - r.close(); - } catch (IOException e) { - throw new Error(e); - } - } - - private TemplateBlock parse(Reader r) throws IOException { - LinkedList splitted = new LinkedList(); - LinkedList commands = new LinkedList(); - StringBuffer buf = new StringBuffer(); - int ch = r.read(); - outer: while (true) { - while (!endsWith(buf, "")) { - buf.append((char) ch); - ch = r.read(); - if (ch == -1) { - throw new EOFException(); - } - } - buf.delete(buf.length() - 2, buf.length()); - String com = buf.toString().replace("\n", ""); - buf.delete(0, buf.length()); - Matcher m = IF_PATTERN.matcher(com); - if (m.matches()) { - final String variable = m.group(1); - final TemplateBlock body = parse(r); - commands.add(new IfStatement(variable, body)); - continue; - } - if (com.matches(" ?\\} ?")) { - break; - } - commands.add(parseCommand(com)); - } - splitted.add(buf.toString()); - String[] contents = splitted.toArray(new String[splitted.size()]); - Outputable[] vars = commands.toArray(new Outputable[commands.size()]); - return new TemplateBlock(contents, vars); - } - - private boolean endsWith(StringBuffer buf, String string) { - return buf.length() >= string.length() - && buf.substring(buf.length() - string.length(), buf.length()).equals(string); - } - - private Outputable parseCommand(String s2) { - if (s2.startsWith("=_")) { - final String raw = s2.substring(2); - return new TranslateCommand(raw); - } else if (s2.startsWith("=$")) { - final String raw = s2.substring(2); - return new OutputVariableCommand(raw); - } else if (s2.startsWith("=s,")) { - String command = s2.substring(3); - final LinkedList store = new LinkedList(); - while (command.startsWith("$")) { - int idx = command.indexOf(","); - store.add(command.substring(0, idx)); - command = command.substring(idx + 1); - } - final String text = command; - return new SprintfCommand(text, store); - } else { - System.out.println("Unknown processing instruction: " + s2); - } - return null; - } - - public void output(PrintWriter out, Language l, Map vars) { - if (source != null && DevelLauncher.DEVEL) { - if (lastLoaded < source.lastModified()) { - try { - System.out.println("Reloading template.... " + source); - InputStreamReader r = new InputStreamReader(new FileInputStream(source), "UTF-8"); - parse(r); - r.close(); - lastLoaded = source.lastModified() + 1000; - } catch (IOException e) { - e.printStackTrace(); - } - } - } - data.output(out, l, vars); - } - - protected static void outputVar(PrintWriter out, Language l, Map vars, String varname) { - Object s = vars.get(varname); - - if (s == null) { - System.out.println("Empty variable: " + varname); - } - if (s instanceof Outputable) { - ((Outputable) s).output(out, l, vars); - } else { - out.print(s); - } - } + + private static class ParseResult { + + TemplateBlock block; + + String endType; + + public ParseResult(TemplateBlock block, String endType) { + this.block = block; + this.endType = endType; + } + + public String getEndType() { + return endType; + } + + public TemplateBlock getBlock(String reqType) { + if (endType == null && reqType == null) { + return block; + } + if (endType == null || reqType == null) { + throw new Error("Invalid block type: " + endType); + } + if (endType.equals(reqType)) { + return block; + } + throw new Error("Invalid block type: " + endType); + } + } + + private TemplateBlock data; + + private long lastLoaded; + + private File source; + + private static final Pattern CONTROL_PATTERN = Pattern.compile(" ?([a-zA-Z]+)\\(\\$([^)]+)\\) ?\\{ ?"); + + private static final Pattern ELSE_PATTERN = Pattern.compile(" ?\\} ?else ?\\{ ?"); + + /** + * Creates a new template by parsing the contents from the given URL. This + * constructor will fail on syntax error. When the URL points to a file, + * {@link File#lastModified()} is monitored for changes of the template. + * + * @param u + * the URL to load the template from. UTF-8 is chosen as charset. + */ + public Template(URL u) { + try { + Reader r = new InputStreamReader(u.openStream(), "UTF-8"); + try { + if (u.getProtocol().equals("file")) { + source = new File(u.toURI()); + lastLoaded = source.lastModified() + 1000; + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + data = parse(r).getBlock(null); + r.close(); + } catch (IOException e) { + throw new Error(e); + } + } + + /** + * Creates a new template by parsing the contents from the given reader. + * This constructor will fail on syntax error. + * + * @param r + * the Reader containing the data. + */ + public Template(Reader r) { + try { + data = parse(r).getBlock(null); + r.close(); + } catch (IOException e) { + throw new Error(e); + } + } + + private ParseResult parse(Reader r) throws IOException { + LinkedList splitted = new LinkedList(); + LinkedList commands = new LinkedList(); + StringBuffer buf = new StringBuffer(); + String blockType = null; + outer: + while (true) { + while ( !endsWith(buf, "")) { + int ch = r.read(); + if (ch == -1) { + throw new EOFException(); + } + buf.append((char) ch); + } + buf.delete(buf.length() - 2, buf.length()); + String com = buf.toString().replace("\n", ""); + buf.delete(0, buf.length()); + Matcher m = CONTROL_PATTERN.matcher(com); + if (m.matches()) { + String type = m.group(1); + String variable = m.group(2); + ParseResult body = parse(r); + if (type.equals("if")) { + if ("else".equals(body.getEndType())) { + commands.add(new IfStatement(variable, body.getBlock("else"), parse(r).getBlock("}"))); + } else { + commands.add(new IfStatement(variable, body.getBlock("}"))); + } + } else if (type.equals("foreach")) { + commands.add(new ForeachStatement(variable, body.getBlock("}"))); + } else { + throw new IOException("Syntax error: unknown control structure: " + type); + } + continue; + } else if ((m = ELSE_PATTERN.matcher(com)).matches()) { + blockType = "else"; + break; + } else if (com.matches(" ?\\} ?")) { + blockType = "}"; + break; + } else { + commands.add(parseCommand(com)); + } + } + splitted.add(buf.toString()); + return new ParseResult(new TemplateBlock(splitted.toArray(new String[splitted.size()]), commands.toArray(new Translatable[commands.size()])), blockType); + } + + private boolean endsWith(StringBuffer buf, String string) { + return buf.length() >= string.length() && buf.substring(buf.length() - string.length(), buf.length()).equals(string); + } + + private Translatable parseCommand(String s2) { + if (s2.startsWith("=_")) { + final String raw = s2.substring(2); + if ( !s2.contains("$") && !s2.contains("!'")) { + return new TranslateCommand(raw); + } else { + return new SprintfCommand(raw); + } + } else if (s2.startsWith("=$")) { + final String raw = s2.substring(2); + return new OutputVariableCommand(raw); + } else { + throw new Error("Unknown processing instruction: " + s2); + } + } + + @Override + public void output(PrintWriter out, Language l, Map vars) { + if (source != null && lastLoaded < source.lastModified()) { + try { + System.out.println("Reloading template.... " + source); + InputStreamReader r = new InputStreamReader(new FileInputStream(source), "UTF-8"); + data = parse(r).getBlock(null); + r.close(); + lastLoaded = source.lastModified() + 1000; + } catch (IOException e) { + e.printStackTrace(); + } + } + data.output(out, l, vars); + } + + protected static void outputVar(PrintWriter out, Language l, Map vars, String varname, boolean unescaped) { + Object s = vars.get(varname); + + if (s == null) { + System.out.println("Empty variable: " + varname); + } + if (s instanceof Outputable) { + ((Outputable) s).output(out, l, vars); + } else if (s instanceof DayDate) { + out.print(DateSelector.getDateFormat().format(((DayDate) s).toDate())); + } else if (s instanceof Date) { + SimpleDateFormat sdfUI = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + out.print(""); + } else { + out.print(s == null ? "null" : (unescaped ? s.toString() : HTMLEncoder.encodeHTML(s.toString()))); + } + } + + public void addTranslations(Collection s) { + data.addTranslations(s); + } }