/******************************************************************************* * Copyright (c) 2005, 2009 committers of openArchitectureWare and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * committers of openArchitectureWare - initial API and implementation *******************************************************************************/ package org.eclipse.xpand2.output; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.internal.xpand2.ast.Statement; import org.eclipse.internal.xpand2.ast.TextStatement; import org.eclipse.internal.xtend.expression.ast.SyntaxElement; import org.eclipse.internal.xtend.util.Pair; import org.eclipse.xpand2.XpandExecutionContext; /** * * * * @author Sven Efftinge (http://www.efftinge.de) * */ public class OutputImpl implements Output, InsertionPointSupport { private boolean automaticHyphenation = false; public void setAutomaticHyphens(final boolean automaticHyphenation) { this.automaticHyphenation = automaticHyphenation; } private static ThreadLocal<Stack<FileHandle>> fileHandles = new ThreadLocal<Stack<FileHandle>>(); // private static ThreadLocal<> private final Map<String, Outlet> outlets = new HashMap<String, Outlet>(); public void addOutlet(final Outlet outlet) { if (outlets.containsKey(outlet.getName())) { if (outlet.getName() == null) throw new IllegalArgumentException("A default outlet is already registered!"); else throw new IllegalArgumentException("An outlet with name " + outlet.getName() + " is already registered!"); } outlets.put(outlet.getName(), outlet); } public Outlet getOutlet(final String name) { return outlets.get(name); } protected FileHandle current() { return getFileHandles().isEmpty() ? null : getFileHandles().peek(); } /** * DO NOT CALL THIS METHOD - FOR TESTS ONLY */ public FileHandle current__testONLY() { return current(); } private boolean deleteLine = false; public void write(final String bytes) { if (current() != null) { if (deleteLine) { final String temp = trimUntilNewline(bytes); removeWSAfterLastNewline(current().getBuffer()); try { ((Appendable) current().getBuffer()).append(temp); } catch (IOException e) { throw new RuntimeException(e); } } else { try { ((Appendable) current().getBuffer()).append(bytes); } catch (IOException e) { throw new RuntimeException(e); } } } deleteLine = false; } public void removeWSAfterLastNewline(final CharSequence cs) { int i = cs.length(); boolean wsOnly = true; for (; i > 0 && wsOnly; i--) { final char c = cs.charAt(i - 1); wsOnly = Character.isWhitespace(c); if (wsOnly && isNewLine(c)) { deleteFromCharSequence(cs, i, cs.length()); return; } } return; } private void deleteFromCharSequence (CharSequence cs, int start, int end) { if (cs instanceof StringBuilder) { ((StringBuilder)cs).delete(start, end); } else if (cs instanceof StringBuffer) { ((StringBuffer)cs).delete(start, end); } else { throw new IllegalArgumentException("Unsupported CharSequence type "+cs.getClass().getName()); } } protected boolean isNewLine(final char c) { return c == '\n' || c == '\r'; } public String trimUntilNewline(final String bytes) { int i = 0; boolean wsOnly = true; for (; i < bytes.length() && wsOnly; i++) { final char c = bytes.charAt(i); wsOnly = Character.isWhitespace(c); if (wsOnly && isNewLine(c)) { if (c == '\r' && i + 1 < bytes.length() && bytes.charAt(i + 1) == '\n') { i++; } return bytes.substring(i + 1); } } return bytes; } private final static Pattern p = Pattern.compile("(.+)://(.+)"); public static Pair<Outlet, String> resolveOutlet(final Map<String, Outlet> allOutlets, String path, String outletName) { if (outletName == null) { final Matcher m = p.matcher(path); if (m.matches()) { outletName = m.group(1); path = m.group(2); } } final Outlet o = allOutlets.get(outletName); if (o == null) { if (outletName == null) throw new IllegalArgumentException("No default outlet was configured!"); else throw new IllegalArgumentException("No outlet with the name " + outletName + " could be found!"); } return new Pair<Outlet, String>(o, path); } public void openFile(final String path, final String outletName) { final Pair<Outlet, String> raw = resolveOutlet(outlets, path, outletName); final Outlet actualOutlet = raw.getFirst(); final String actualPath = raw.getSecond(); getFileHandles().push(actualOutlet.createFileHandle(actualPath)); } public void closeFile() { final FileHandle fi = getFileHandles().pop(); fi.writeAndClose(); } private final Stack<SyntaxElement> s = new Stack<SyntaxElement>(); public void pushStatement(final SyntaxElement stmt, final XpandExecutionContext ctx) { if (stmt instanceof TextStatement) { deleteLine = ((TextStatement) stmt).isDeleteLine(); if (automaticHyphenation) { deleteLine = true; } } s.push(stmt); } public SyntaxElement popStatement() { final SyntaxElement se = s.pop(); return se; } protected Stack<FileHandle> getFileHandles() { Stack<FileHandle> result = fileHandles.get(); if (result == null) { result = new Stack<FileHandle>(); fileHandles.set(result); } return result; } public void activateInsertionPoint(Statement stmt) { if (current() != null) { if (!(current() instanceof InsertionPointSupport)) { throw new IllegalStateException ("Current handle does not implement InsertionPointSupport."); } ((InsertionPointSupport)current()).activateInsertionPoint(stmt); } } public void deactivateInsertionPoint(Statement stmt) { if (current() != null) { if (!(current() instanceof InsertionPointSupport)) { throw new IllegalStateException ("Current handle does not implement InsertionPointSupport."); } ((InsertionPointSupport)current()).deactivateInsertionPoint(stmt); } } public void registerInsertionPoint(Statement stmt) { if (!(current() instanceof InsertionPointSupport)) { throw new IllegalStateException ("Current handle does not implement InsertionPointSupport."); } ((InsertionPointSupport)current()).registerInsertionPoint(stmt); } }