protected List<Code> compile( final Mustache m, final Reader br, String tag, final AtomicInteger currentLine, String file) throws MustacheException { final List<Code> list = new LinkedList<Code>(); // Now we grab the mustache template String sm = "{{"; String em = "}}"; int c; boolean onlywhitespace = true; boolean iterable = currentLine.get() != 0; currentLine.compareAndSet(0, 1); StringBuilder out = new StringBuilder(); try { while ((c = br.read()) != -1) { if (c == '\r') { continue; } // Increment the line if (c == '\n') { currentLine.incrementAndGet(); if (!iterable || (iterable && !onlywhitespace)) { out.append("\n"); } out = write(list, out, currentLine.intValue()); iterable = false; onlywhitespace = true; continue; } // Check for a mustache start if (c == sm.charAt(0)) { br.mark(1); if (br.read() == sm.charAt(1)) { // Two mustaches, now capture command StringBuilder sb = new StringBuilder(); while ((c = br.read()) != -1) { br.mark(1); if (c == em.charAt(0)) { if (br.read() == em.charAt(1)) { // Matched end break; } else { // Only one br.reset(); } } sb.append((char) c); } final String command = sb.toString().trim(); final char ch = command.charAt(0); final String variable = command.substring(1).trim(); switch (ch) { case '#': case '^': case '_': case '<': case '$': case '=': case '?': { int start = currentLine.get(); final List<Code> codes = compile(m, br, variable, currentLine, file); int lines = currentLine.get() - start; if (!onlywhitespace || lines == 0) { write(list, out, currentLine.intValue()); } out = new StringBuilder(); switch (ch) { case '#': list.add(cf.iterable(m, variable, codes, file, currentLine.get())); break; case '^': list.add(cf.notIterable(m, variable, codes, file, currentLine.get())); break; case '?': list.add(cf.ifIterable(m, variable, codes, file, currentLine.get())); break; case '_': list.add(cf.function(m, variable, codes, file, currentLine.get())); break; case '<': list.add(cf.extend(m, variable, codes, file, currentLine.get())); break; case '$': list.add(cf.name(m, variable, codes, file, currentLine.get())); break; } iterable = lines != 0; break; } case '/': { // Tag end if (!onlywhitespace) { write(list, out, currentLine.intValue()); } if (!variable.equals(tag)) { throw new MustacheException( "Mismatched start/end tags: " + tag + " != " + variable + " in " + file + ":" + currentLine); } return list; } case '>': { out = write(list, out, currentLine.intValue()); list.add(cf.partial(m, variable, file, currentLine.get())); break; } case '{': { out = write(list, out, currentLine.intValue()); // Not escaped String name = variable; if (em.charAt(1) != '}') { name = variable.substring(0, variable.length() - 1); } else { if (br.read() != '}') { throw new MustacheException( "Improperly closed variable in " + file + ":" + currentLine); } } final String finalName = name; list.add(cf.value(m, finalName, false, currentLine.intValue())); break; } case '&': { // Not escaped out = write(list, out, currentLine.intValue()); list.add(cf.value(m, variable, false, currentLine.intValue())); break; } case '%': // Pragmas out = write(list, out, currentLine.intValue()); break; case '!': // Comment out = write(list, out, currentLine.intValue()); break; default: { // Reference out = write(list, out, currentLine.intValue()); list.add(cf.value(m, command, true, currentLine.intValue())); break; } } continue; } else { // Only one br.reset(); } } onlywhitespace = (c == ' ' || c == '\t') && onlywhitespace; out.append((char) c); } write(list, out, currentLine.intValue()); br.close(); } catch (IOException e) { throw new MustacheException("Failed to read", e); } list.add(cf.eof(currentLine.intValue())); return list; }