petermr avatar petermr committed 8eb2cc4

committing jc-templpate-parser - sorry folks!

Comments (0)

Files changed (192)

+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" output="target/classes" path="src/main/java"/>
+	<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
+	<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>jc-template-parser</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.m2e.core.maven2Builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.m2e.core.maven2Nature</nature>
+	</natures>
+</projectDescription>

.settings/org.eclipse.core.resources.prefs

+#Thu Mar 01 18:57:39 GMT 2012
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding/<project>=UTF-8

.settings/org.eclipse.jdt.core.prefs

+#Thu Mar 01 18:57:39 GMT 2012
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6

.settings/org.eclipse.m2e.core.prefs

+#Thu Mar 01 18:57:35 GMT 2012
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1

src/main/java/org/xmlcml/cml/converters/templates/output/AbstractTransformConverter.java

 
 	private static Logger LOG = Logger.getLogger(AbstractTransformConverter.class);
 
-	protected static Template transformTemplate = null;
+	protected static MarkupContainer transformTemplate = null;
 	
 	public MimeType getInputType() {
 		return MimeType.XML;
 		this.setTemplate(new Template(templateElement));
 	}
 
-	protected void setTemplate(Template template) {
+	protected void setTemplate(MarkupContainer template) {
 		this.transformTemplate = template;
 	}
 

src/main/java/org/xmlcml/cml/converters/templates/output/Debug.java

 	public static final String TAG = "debug";
 
 	private LineContainer lineContainer;
-	private Template template;
+	private MarkupContainer template;
 	
-	public Debug(Template template) {
+	public Debug(MarkupContainer template) {
 		this.template = template;
 	}
 

src/main/java/org/xmlcml/cml/converters/templates/output/LineReader.java

 import org.xmlcml.cml.element.CMLScalar;
 import org.xmlcml.euclid.JodaDate;
 import org.xmlcml.euclid.Real;
+import org.xmlcml.euclid.Util;
 
 public abstract class LineReader extends Element implements MarkupApplier {
 
 	private static final String ID = "id";
 
 	protected static final String ELEMENT_TYPE = "elementType";
+	private static final String MATRIXFIELDS = "matrixFields";
 	private static final String FORMAT_TYPE = "formatType";
 	private static final String NEWLINE = "newline";
 	private static final String MULTIPLE = "multiple"; // deprecated
 	private static final FormatType DEFAULT_FORMAT_TYPE = FormatType.NONE;
 	private static final String REPEAT = "repeat";
 	private static final String REPEAT_COUNT = "repeatCount"; // deprecated
+
+	//
 	private static String DEFAULT_CONTENT = "{X}";
 
 
 	}
 	 public enum LineType {
 		COMMENT("comment"),
+		MATRIX("matrix"),
 		READLINES("readLines"),
-		RECORD("record"),
+		RECORD("record"), 
 		;
 
         private final String tag;
 	protected String content;
 	private   int fieldCount;
 	protected String localDictRef;
+	protected String[] fields;
 	protected int currentCharInLine;
 	protected Integer columns;
 	protected Integer rows;
 	protected String names;
 	protected LineContainer lineContainer;
 	protected OutputLevel outputLevel;
-	protected Template template;
+	protected MarkupContainer template;
 	protected RegexProcessor regexProcessor;
 
 	public String getId() {
 		fieldList = new ArrayList<Field>();
 	}
 
-	public LineReader(String tag, Element lineReaderElement, Template template) {
+	public LineReader(String tag, Element lineReaderElement, MarkupContainer template) {
 		super(tag);
 		init();
 		this.template = template;
 		return fieldList;
 	}
 
+	public String[] getMatrixFields() {
+		return fields;
+	}
+
 	public int getFieldCount() {
 		return fieldCount;
 	}
 	private void processAttributesAndContent() {
 		Template.checkIfAttributeNamesAreAllowed(lineReaderElement, new String[]{
 			ID,
+			MATRIXFIELDS,
 			FORMAT_TYPE,
 			LINES_TO_READ,
 			MAKE_ARRAY,
 		processAttributeFormatType();
 		processAttributeLinesToRead();
 		processAttributeRepeatCount();
+		processAttributeMatrixFields();
 		processAttributeNames();
 		processAttributeMakeArrays();
 		processAttributeId();
 		}
 	}
 
+	private void processAttributeMatrixFields() {
+		fields = null;
+		String fieldsS = lineReaderElement.getAttributeValue(MATRIXFIELDS);
+		if (fieldsS != null) {
+			fields = fieldsS.toLowerCase().split(Util.S_WHITEREGEX);
+			for (String field : fields) {
+				if (!Util.containsString(Matrix.MATRIXROLES, field)) {
+					throw new RuntimeException("matrixfields cannot contain: "+field);
+				}
+			}
+			this.addAttribute(new Attribute(MATRIXFIELDS, fieldsS));
+		}
+	}
+
 	private void processAttributeFormatType() {
 		formatType = null;
 		String formatTypeS = lineReaderElement.getAttributeValue(FORMAT_TYPE);

src/main/java/org/xmlcml/cml/converters/templates/output/MarkupContainer.java

+package org.xmlcml.cml.converters.templates.output;
+
+import java.util.List;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.xmlcml.cml.base.CMLConstants;
+import org.xmlcml.cml.base.CMLUtil;
+import org.xmlcml.cml.converters.Outputter.OutputLevel;
+
+import nu.xom.Element;
+
+public abstract class MarkupContainer implements MarkupApplier {
+	private final static Logger LOG = Logger.getLogger(MarkupContainer.class);
+	static{
+	    LOG.setLevel(Level.ERROR);
+	}
+
+	private static final String HTTP_WWW_W3_ORG_2001_X_INCLUDE = "http://www.w3.org/2001/XInclude";
+	protected static final String DEBUG = "debug";
+	protected static final String DICT_REF = "dictRef";
+	protected static final String ID = "id";
+	protected static final String NULL_ID = "NULL_ID";
+	protected static final String NEWLINE = "newline";
+	private static final String DEFAULT_NEWLINE = CMLConstants.S_DOLLAR;
+	protected static final String MULTIPLE = "multiple"; 
+	
+	protected String id;
+	protected String name;
+	protected List<MarkupApplier> markerList;
+	protected LineContainer lineContainer;
+	protected Element templateElement1;
+	protected OutputLevel outputLevel;
+	protected String dictRef;
+	protected boolean debug = false;
+	protected String newlineS;
+
+	public MarkupContainer() {
+		
+	}
+	
+	public MarkupContainer(Element element) {
+		this.templateElement1 = element;
+	}
+
+	protected void processChildElementsAndAttributes() {
+		processAttributes();
+		createSubclassedElementsFromChildElements();
+		if (!OutputLevel.NONE.equals(outputLevel)) {
+//			this.debug();
+		}
+	}
+
+	protected abstract void processAttributes();
+	protected abstract void createSubclassedElementsFromChildElements();
+	
+	public String getId() {
+		return id;
+	}
+
+	protected void ignore() {
+	}
+
+	public void debug() {
+		// TODO Auto-generated method stub
+		
+	}
+
+	protected void copyNamespaces(Element targetElement) {
+		copyNamespaces(this.templateElement1, targetElement);
+	}
+
+	protected void processException(LineContainer lineContainer, MarkupApplier marker, Exception e) {
+	//		lineContainer.debug("LINE CONTAINER");
+			String line = lineContainer.peekLine();
+			int nline = lineContainer.getCurrentNodeIndex();
+			System.err.println("PREVIOUS..."+nline);
+			for (int i = (Math.max(0, nline-6)); i < nline; i++) {
+				System.err.println(lineContainer.getLinesElement().getChild(i));
+			}
+			System.out.println("========DEBUG======="+marker.getId()+">>>>>");
+	//		CMLUtil.debug(templateElement1, "TEMPLATE");
+			System.out.println("<<<<<"+marker.getId()+"========DEBUG=======");
+			if (line == null) {
+				LOG.error("Null line ("+nline+")", e);
+			} else {
+				throw new RuntimeException("Failed; current line ("+nline+")"+line, e);
+			}
+		}
+
+	public static void copyNamespaces(Element fromElement, Element targetElement) {
+		int count = fromElement.getNamespaceDeclarationCount();
+		for (int i = 0; i < count; i++) {
+			String prefix = fromElement.getNamespacePrefix(i);
+			String namespaceURI = fromElement.getNamespaceURI(prefix);
+			if (!(HTTP_WWW_W3_ORG_2001_X_INCLUDE.equals(namespaceURI))) {
+				if (!hasNamespacePrefix(targetElement, prefix)) {
+					targetElement.addNamespaceDeclaration(prefix, namespaceURI);
+				}
+			}
+		}
+	}
+
+	private static boolean hasNamespacePrefix(Element targetElement, String prefix) {
+		for (int i = 0; i < targetElement.getNamespaceDeclarationCount(); i++) {
+			if (prefix.equals(targetElement.getNamespacePrefix(i))) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	public static void checkIfAttributeNamesAreAllowed(Element theElement,
+		String[] allowedNames) {
+			for (int i = 0; i < theElement.getAttributeCount(); i++) {
+				String attName = theElement.getAttribute(i).getLocalName();
+				boolean allowed = false;
+				Exception exception = null;
+				for (String allowedName : allowedNames) {
+					if (attName.equals(allowedName)) {
+						allowed = true;
+						break;
+					}
+				}
+				if (!allowed) {
+					System.out.println("Allowed options:");
+					for (String name : allowedNames) {
+						System.out.println(">     "+name);
+					}
+					CMLUtil.debug(theElement, "FORBIDDEN ATT "+attName);
+					throw new RuntimeException("Forbidden attribute name: "+attName);
+				}
+			}
+		}
+
+	public static String escape(String s) {
+		if ("\"$%^*-+.".contains(s)) {
+			s =  CMLConstants.S_BACKSLASH+s;
+		}
+		return s;
+	}
+
+	protected void processNewline() {
+		newlineS = templateElement1.getAttributeValue(NEWLINE);
+		if (newlineS == null) {
+			newlineS = templateElement1.getAttributeValue(MULTIPLE);
+			if (newlineS != null) {
+				LOG.warn("multiple is deprecated, use newline");
+			}
+		}
+		if (newlineS == null) {
+			newlineS = DEFAULT_NEWLINE;
+		}
+		if (newlineS != null) {
+			newlineS = escape(newlineS);
+		}
+	}
+
+
+}

src/main/java/org/xmlcml/cml/converters/templates/output/Matrix.java

+package org.xmlcml.cml.converters.templates.output;
+
+import java.util.ArrayList;
+
+import nu.xom.Attribute;
+import nu.xom.Element;
+import nu.xom.Elements;
+import nu.xom.Nodes;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.xmlcml.cml.base.CMLElement;
+import org.xmlcml.cml.base.CMLUtil;
+import org.xmlcml.cml.converters.Outputter;
+import org.xmlcml.cml.converters.Outputter.OutputLevel;
+import org.xmlcml.cml.converters.templates.output.LineReader.LineType;
+import org.xmlcml.cml.element.CMLArray;
+import org.xmlcml.cml.element.CMLList;
+import org.xmlcml.cml.element.CMLMatrix;
+import org.xmlcml.cml.element.CMLScalar;
+import org.xmlcml.euclid.Util;
+
+public class Matrix extends MarkupContainer {
+	private final static Logger LOG = Logger.getLogger(Matrix.class);
+	static {
+		LOG.setLevel(Level.DEBUG);
+	}
+
+	private static final String ROLE = "role";
+	private static final String MATRIX = "__matrix";
+	public static final String MATRIX_READER = "matrixReader";
+
+	private static final String MATRIX_CONTAINER = "matrixContainer";
+	private static final String BODY = "body";
+	private static final String FOOTER = "footer";
+	private static final String HEADER = "header";
+	private static final String LEFT = "left";
+	private static final String RIGHT = "right";
+	static final String[] MATRIXROLES = {BODY, FOOTER, HEADER, LEFT, RIGHT};
+	
+	private static String prefix = "ChangeMe";
+
+	private Integer rows = null;
+	private Integer cols = null;
+
+	private CMLArray matrixArray0;
+	private String dataType;
+	private CMLMatrix matrix;
+	private double[] dvals;
+	private int[] ivals;
+
+	private String[] matrixFields;
+
+	private Element linesElements;
+	private CMLList headerList;
+	private CMLList bodyList;
+	private CMLList footerList;
+
+	private CMLList matrixContainer;
+
+	private String expected;
+	private Element linesElement;
+	private Nodes matrixArrayNodes;
+	
+	public Matrix(Element element) {
+		this.templateElement1 = element;
+		processChildElementsAndAttributes();
+	}
+	
+	protected void processAttributes() {
+		checkIfAttributeNamesAreAllowed(templateElement1, new String[]{
+			DEBUG,
+			DICT_REF,
+			ID,
+		});
+				
+		id = templateElement1.getAttributeValue(ID);
+		if (id == null) {
+			id = NULL_ID;
+		}
+		this.dictRef = templateElement1.getAttributeValue(DICT_REF);
+		
+		processNewline();
+		
+		outputLevel = Outputter.extractOutputLevel(this.templateElement1);
+		LOG.trace(outputLevel+"/"+this.templateElement1.getAttributeValue(Outputter.OUTPUT));
+		if (!OutputLevel.NONE.equals(outputLevel)) {
+//			System.out.println("OUTPUT "+outputLevel);
+		}
+		debug = (templateElement1.getAttributeValue(DEBUG) != null);
+	}
+
+
+	protected void createSubclassedElementsFromChildElements() {
+		Elements childElements = templateElement1.getChildElements();
+		markerList = new ArrayList<MarkupApplier>();
+		for (int i = 0; i < childElements.size(); i++) {
+			LineReader lineReader = null;
+			Element childElement = (Element) childElements.get(i);
+			String name = childElement.getLocalName();
+			if (name == null) {
+				ignore();
+			} else if (LineType.COMMENT.getTag().equals(name)) {
+				ignore();
+			} else if (LineType.RECORD.getTag().equals(name)) {
+				lineReader = new RecordReader(childElement, this);
+				markerList.add(lineReader);
+			} else if (MarkupContainer.DEBUG.equals(name)) {
+				markerList.add(new Debug(this));
+			} else {
+				MarkupApplier transformer = TransformElement.createTransformer(childElement);
+				if (transformer != null) {
+					markerList.add(transformer);
+				} else {
+					CMLUtil.debug(templateElement1, "UNKNOWN CHILD");
+					throw new RuntimeException("unknown child: "+name);
+				}
+			}
+		}
+	}
+
+
+	@Override
+	public void applyMarkup(LineContainer lineContainer) {
+		this.lineContainer = lineContainer;
+		CMLElement element = null;
+		int originalNodeIndex = lineContainer.getCurrentNodeIndex();
+		linesElement = lineContainer.getLinesElement();
+		copyNamespaces(linesElement);
+		CMLElement.addCMLXAttribute(linesElement, Template.TEMPLATE_REF, this.getId());
+		matrixContainer = new CMLList();
+		headerList = createListAndAddToMatrixContainer(HEADER);
+		bodyList = createListAndAddToMatrixContainer(BODY);
+		footerList = createListAndAddToMatrixContainer(FOOTER);
+		CMLElement.addCMLXAttribute(matrixContainer, ROLE, MATRIX_CONTAINER);
+		expected = HEADER;
+		for (MarkupApplier marker : markerList) {
+			LOG.info("Applying: "+marker.getClass().getSimpleName()+" "+marker.getId());
+			try {
+				if (marker instanceof RecordReader) {
+					processRecord(lineContainer, (RecordReader) marker);
+				} else {
+					processNonRecord(lineContainer, marker);
+				}
+			} catch (Exception e) {
+				processException(lineContainer, marker, e);
+			}
+		}
+		tidyMatrix();
+	}
+
+	private CMLList createListAndAddToMatrixContainer(String role) {
+		CMLList list = new CMLList();
+		CMLElement.addCMLXAttribute(list, ROLE, role);
+		matrixContainer.appendChild(list);
+		return list;
+	}
+
+	private void processRecord(LineContainer lineContainer, RecordReader recordReader) {
+		// standard processing at present but might be optimized later
+		recordReader.applyMarkup(lineContainer);
+		processFields(recordReader, lineContainer);
+	}
+
+	private void processFields(RecordReader recordReader, LineContainer lineContainer) {
+			matrixFields = recordReader.getMatrixFields();
+			linesElements = lineContainer.getLinesElement();
+			if (HEADER.equals(expected)) {
+				if (Util.containsString(matrixFields, HEADER)) {
+					processHeaderRecord();
+				} else if (Util.containsString(matrixFields, BODY)) {
+					expected = BODY;
+					processBodyRecord();
+				} else if (Util.containsString(matrixFields, FOOTER)) {
+					throw new RuntimeException("Missing body");
+				}
+			} else if (BODY.equals(expected)) {
+				if (Util.containsString(matrixFields, BODY)) {
+					throw new RuntimeException("Can only have one body");
+				} else if (Util.containsString(matrixFields, FOOTER)) {
+					expected = FOOTER;
+					processFooterRecord();
+				} else {
+					throw new RuntimeException("Bad processing order:"+expected);
+				}
+			} else if (FOOTER.equals(expected)) {
+				if (Util.containsString(matrixFields, FOOTER)) {
+					processFooterRecord();
+				} else {
+					throw new RuntimeException("Bad processing order:"+expected);
+				}
+			} else {
+				throw new RuntimeException("all <record> children of <matrix> must have known fields");
+			}
+	}
+
+	private void processHeaderRecord() {
+		ensureHeaderList();
+		addArrayOrScalar(headerList, HEADER);
+	}
+
+	private void addArrayOrScalar(CMLList hfList, String role) {
+		Nodes listNodes = linesElements.query("./cml:list[cml:array | cml:scalar]", CMLUtil.CML_XPATH);
+		if (listNodes.size() == 0) {
+			throw new RuntimeException("empty list");
+		}
+		CMLElement list = (CMLElement) listNodes.get(0);
+		Nodes childNodes = list.query("./cml:array | ./cml:scalar", CMLUtil.CML_XPATH);
+		if (childNodes.size() != 1) {
+			throw new RuntimeException("Header/footer record must have exactly one <scalar> or one <array>");
+		}
+		CMLElement childNode = (CMLElement) childNodes.get(0);
+		childNode.detach();
+		hfList.appendChild(childNode);
+		hfList.detach();
+		matrixContainer.appendChild((CMLElement)hfList);
+	}
+
+	private void ensureHeaderList() {
+		if (headerList == null) {
+			headerList = new CMLList();
+			CMLElement.addCMLXAttribute(headerList, ROLE, HEADER);
+		}
+	}
+
+	private void processBodyRecord() {
+		Nodes listNodes = linesElements.query(".//cml:list[cml:array]", CMLUtil.CML_XPATH);
+		Element list = (Element) listNodes.get(0);
+		if (list == null) {
+			throw new RuntimeException("body requires arrays with list parent");
+		}
+		if (matrix == null) {
+			createMatrix(list, listNodes);
+		}
+	}
+
+	private void processFooterRecord() {
+		ensureFooterList();
+		addArrayOrScalar(footerList, FOOTER);
+	}
+
+	private void ensureFooterList() {
+		if (footerList == null) {
+			footerList = new CMLList();
+			CMLElement.addCMLXAttribute(footerList, ROLE, FOOTER);
+		}
+	}
+
+	private void processNonRecord(LineContainer lineContainer, MarkupApplier marker) {
+		marker.applyMarkup(lineContainer);
+		Element linesContainer = lineContainer.getLinesElement();
+		linesContainer.detach();
+		matrixContainer.appendChild(linesContainer);
+	}
+
+	private void createMatrix(Element list, Nodes listNodes) {
+		matrixArrayNodes = listNodes.get(0).query("./cml:array", CMLUtil.CML_XPATH);
+		matrixArray0 = (CMLArray) matrixArrayNodes.get(0);
+		cols = matrixArray0.getSize();
+		rows  = matrixArrayNodes.size();
+		dataType = matrixArray0.getDataType();
+		prepareForNumericValues();
+		extractNumericValues(matrixArrayNodes);
+		createAndPopulateMatrix();
+		matrix.setDictRef(matrixArray0.getDictRef());
+//		list.getParent().replaceChild(list, matrix);
+		list.appendChild(matrix);
+		listNodes.get(0).detach();
+	}
+
+	private void createAndPopulateMatrix() {
+		if (CMLUtil.XSD_DOUBLE.equals(dataType)) {
+			matrix = new CMLMatrix(rows, cols, dvals);
+		} else if (CMLUtil.XSD_INTEGER.equals(dataType)) {
+			matrix = new CMLMatrix(rows, cols, ivals);
+		} else {
+			throw new RuntimeException("cannot create non-numeric matrix");
+		}
+	}
+
+	private void extractNumericValues(Nodes arrayNodes) {
+		for (int irow = 0; irow <  arrayNodes.size(); irow++) {
+			CMLArray array = (CMLArray) arrayNodes.get(irow);
+			if (CMLUtil.XSD_DOUBLE.equals(dataType)) {
+				System.arraycopy(array.getDoubles(), 0, dvals, irow * cols, cols);
+			} else if (CMLUtil.XSD_INTEGER.equals(dataType)) {
+				System.arraycopy(array.getInts(), 0, ivals, irow * cols, cols);
+			}
+		}
+	}
+
+	private void prepareForNumericValues() {
+		if (CMLUtil.XSD_DOUBLE.equals(dataType)) {
+			dvals = new double[rows * cols];
+		} else if (CMLUtil.XSD_INTEGER.equals(dataType)) {
+			ivals = new int[rows * cols];
+		} else {
+			throw new RuntimeException("cannot create non-numeric matrix");
+		}
+	}
+
+	private void tidyMatrix() {
+		linesElement.appendChild(matrixContainer);
+		matrix.detach();
+		// insert after header
+		int headerRole = matrixContainer.query("./cml:list[@*[local-name()='role' and .='header']]", CMLUtil.CML_XPATH).size();
+		matrixContainer.insertChild(matrix, headerRole);
+	}
+
+
+	@Override
+	public void applyMarkup(Element element) {
+		// TODO Auto-generated method stub
+		
+	}
+
+
+
+
+
+}

src/main/java/org/xmlcml/cml/converters/templates/output/RecordReader.java

 
 	private static String prefix = "ChangeMe";
 	
-	public RecordReader(Element recordReaderElement, Template template) {
+	public RecordReader(Element recordReaderElement, MarkupContainer template) {
 		super(RECORD_READER, recordReaderElement, template);
 		init0();
 	}

src/main/java/org/xmlcml/cml/converters/templates/output/Template.java

 import org.xmlcml.cml.element.CMLScalar;
 import org.xmlcml.euclid.Int2;
 
-public class Template implements MarkupApplier {
-	private static final String HTTP_WWW_W3_ORG_2001_X_INCLUDE = "http://www.w3.org/2001/XInclude";
-	private final static Logger LOG = Logger.getLogger(Template.class);
+public class Template extends MarkupContainer {
+	final static Logger LOG = Logger.getLogger(Template.class);
 	
 	static{
 	    LOG.setLevel(Level.ERROR);
 	// attributes
 	public static final String CMLX_UNREAD = "cmlx:unread";
 	public static final String CONVENTION = "convention";
-	private static final String DICT_REF = "dictRef";
 	private static final String END_OFFSET = "endOffset";
 	private static final String END_PATTERN = "endPattern";
-	private static final String ID = "id";
-	private static final String MULTIPLE = "multiple"; // deprecated
 	private static final String NAME = "name";
 	private static final String NAMES = "names";
-	private static final String NEWLINE = "newline";
 	private static final String OFFSET = "offset";
 	private static final String OUTPUT = "output";
 	private static final String PATTERN = "pattern";
 	private static final String REPEAT_COUNT = "repeatCount"; // deprecated
 	public  static final String TEMPLATE_REF = "templateRef";
 
-	private static final String DEBUG = "debug"; // maybe somewhere better?
 	private static final String BASE = "base"; // left by XInclude
 
-	private static final String NULL_ID = "NULL_ID";
-	private static final String DEFAULT_NEWLINE = CMLConstants.S_DOLLAR;
-
 	public static XPathContext CML_CMLX_CONTEXT = null;
 	static {
 		CML_CMLX_CONTEXT = new XPathContext();
 		CML_CMLX_CONTEXT.addNamespace(CMLConstants.CMLX_PREFIX, CMLConstants.CMLX_NS);
 	}
 	
-	protected Element templateElement1;
-	private String id;
-	private String name;
-	private String newlineS;
 	private String patternString;
 	private String endPatternString;
 	private PatternContainer endChunker;
 	protected PatternContainer startChunker;
-	private boolean debug = false;
 	private Integer offset;
 	private Integer endOffset;
 	private Integer minRepeatCount = 1;
 	private Integer maxRepeatCount = 1;
-	private OutputLevel outputLevel;
-
-	private List<MarkupApplier> markerList;
-	private LineContainer lineContainer;
-	private String dictRef;
 	private String[] names;
 	private List<DictionaryContainer> dictionaryList;
 
+	public Template() {
+		super();
+	}
 	public Template(Element element) {
-		this.templateElement1 = element;
+		super(element);
         try {
             ClassPathXIncludeResolver.resolveIncludes(element.getDocument());
 		} catch (Exception e) {
 		processChildElementsAndAttributes();
 	}
 
-	private void processChildElementsAndAttributes() {
-		processAttributes();
-		createSubclassedElementsFromChildElements();
-		if (!OutputLevel.NONE.equals(outputLevel)) {
-//			this.debug();
-		}
-	}
-
-	public String getId() {
-		return id;
-	}
-
 	public String getName() {
 		return name;
 	}
 		return lineContainer;
 	}
 
-	private void processAttributes() {
+	protected void processAttributes() {
 		checkIfAttributeNamesAreAllowed(templateElement1, new String[]{
 			BASE,
 			CONVENTION,
 		endChunker = new PatternContainer(endPatternString, newlineS, endOffset);
 	}
 
-	private void processNewline() {
-		newlineS = templateElement1.getAttributeValue(NEWLINE);
-		if (newlineS == null) {
-			newlineS = templateElement1.getAttributeValue(MULTIPLE);
-			if (newlineS != null) {
-				LOG.warn("multiple is deprecated, use newline");
-			}
-		}
-		if (newlineS == null) {
-			newlineS = DEFAULT_NEWLINE;
-		}
-		if (newlineS != null) {
-			newlineS = escape(newlineS);
-		}
-	}
-
-	public static String escape(String s) {
-		if ("\"$%^*-+.".contains(s)) {
-			s =  CMLConstants.S_BACKSLASH+s;
-		}
-		return s;
-	}
-
 	private String[] getStringsFromAttribute(String names) {
 		return names == null ? null : names.split(CMLConstants.S_WHITEREGEX);
 	}
 		}
 	}
 
-	public static void checkIfAttributeNamesAreAllowed(Element theElement, String[] allowedNames) {
-		for (int i = 0; i < theElement.getAttributeCount(); i++) {
-			String attName = theElement.getAttribute(i).getLocalName();
-			boolean allowed = false;
-			Exception exception = null;
-			for (String allowedName : allowedNames) {
-				if (attName.equals(allowedName)) {
-					allowed = true;
-					break;
-				}
-			}
-			if (!allowed) {
-				System.out.println("Allowed options:");
-				for (String name : allowedNames) {
-					System.out.println(">     "+name);
-				}
-				CMLUtil.debug(theElement, "FORBIDDEN ATT "+attName);
-				throw new RuntimeException("Forbidden attribute name: "+attName);
-			}
-		}
-	}
-
-
-	private void createSubclassedElementsFromChildElements() {
+	protected void createSubclassedElementsFromChildElements() {
 		Elements childElements = templateElement1.getChildElements();
 		markerList = new ArrayList<MarkupApplier>();
 		for (int i = 0; i < childElements.size(); i++) {
 			} else if (LineType.RECORD.getTag().equals(name)) {
 				lineReader = new RecordReader(childElement, this);
 				markerList.add(lineReader);
+			} else if (LineType.MATRIX.getTag().equals(name)) {
+				Matrix matrix = new Matrix(childElement);
+				markerList.add(matrix);
 			} else if (Template.TAG.equals(name)) {
 				System.out.println(templateElement1.getAttributeValue(ID)+"/"+childElement.getAttributeValue(ID));
 				throw new RuntimeException("Template cannot be child of Template; use templateList");
 			} else if (TemplateListElement.TAG.equals(name)) {
 				TemplateListElement templateContainer = new TemplateListElement(childElement);
 				markerList.add(templateContainer);
-			} else if (Template.DEBUG.equals(name)) {
+			} else if (MarkupContainer.DEBUG.equals(name)) {
 				markerList.add(new Debug(this));
 			} else {
 				MarkupApplier transformer = TransformElement.createTransformer(childElement);
 		this.getDictionaryContainerList().add(dictionary);
 	}
 
-	private void ignore() {
-		// TODO Auto-generated method stub
-		
-	}
+	
 
 	// =========================OUTPUT/MARKUP===============================
 	
 	}
 
 
-	private void processException(LineContainer lineContainer,
-			MarkupApplier marker, Exception e) {
-//		lineContainer.debug("LINE CONTAINER");
-		String line = lineContainer.peekLine();
-		int nline = lineContainer.getCurrentNodeIndex();
-		System.err.println("PREVIOUS..."+nline);
-		for (int i = (Math.max(0, nline-6)); i < nline; i++) {
-			System.err.println(lineContainer.getLinesElement().getChild(i));
-		}
-		System.out.println("========DEBUG======="+marker.getId()+">>>>>");
-//		CMLUtil.debug(templateElement1, "TEMPLATE");
-		System.out.println("<<<<<"+marker.getId()+"========DEBUG=======");
-		if (line == null) {
-			if (debug) {
-				LOG.error("Null line ("+nline+")", e);
-			} else {
-//				new Exception().printStackTrace();
-				LOG.error("Null line ("+nline+")", e);
-			}
-		} else {
-			throw new RuntimeException("Failed; current line ("+nline+")"+line, e);
-		}
-	}
-	
 	public void applyMarkup(Element element) {
 		copyNamespaces(element);
 		CMLElement.addCMLXAttribute(element, Template.TEMPLATE_REF, this.getId());
 		}
 	}
 	
-	private void copyNamespaces(Element targetElement) {
-		copyNamespaces(this.templateElement1, targetElement);
-	}
-
-	public static void copyNamespaces(Element fromElement, Element targetElement) {
-		int count = fromElement.getNamespaceDeclarationCount();
-		for (int i = 0; i < count; i++) {
-			String prefix = fromElement.getNamespacePrefix(i);
-			String namespaceURI = fromElement.getNamespaceURI(prefix);
-			if (!(HTTP_WWW_W3_ORG_2001_X_INCLUDE.equals(namespaceURI))) {
-				if (!hasNamespacePrefix(targetElement, prefix)) {
-					targetElement.addNamespaceDeclaration(prefix, namespaceURI);
-				}
-			}
-		}
-	}
-
-	private static boolean hasNamespacePrefix(Element targetElement, String prefix) {
-		for (int i = 0; i < targetElement.getNamespaceDeclarationCount(); i++) {
-			if (prefix.equals(targetElement.getNamespacePrefix(i))) {
-				return true;
-			}
-		}
-		return false;
-	}
-
 	public List<Element> resetNodeIndexAndApplyChunkers(LineContainer lineContainer) {
 //		if (resetCounter) {
 			lineContainer.setCurrentNodeIndex(0);

src/main/java/org/xmlcml/cml/converters/templates/output/TemplateListElement.java

 	
 	public void debug() {
 		LOG.debug("DEBUG TemplateList: "+templateList.size());
-		for (Template template : templateList) {
+		for (MarkupContainer template : templateList) {
 			template.debug();
 		}
 	}

src/test/java/org/xmlcml/cml/converters/templates/MatrixTest.java

+package org.xmlcml.cml.converters.templates;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.xmlcml.cml.base.CMLUtil;
+import org.xmlcml.cml.converters.templates.output.LineContainer;
+import org.xmlcml.cml.converters.templates.output.MarkupContainer;
+import org.xmlcml.cml.converters.templates.output.Template;
+import org.xmlcml.cml.testutil.JumboTestUtils;
+
+public class MatrixTest {
+
+	@Test
+	public void testMatrix0() {
+		String templateS = 
+				"<template>" +
+				"  <matrix/>" +
+				"</template>";
+			MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
+			Assert.assertNotNull("template", template);
+	}
+	
+	@Test
+	public void testRow() {
+		String templateS = 
+				"<template>" +
+				"  <matrix>" +
+				"    <record id='r1' repeat='*'/>" +
+				"  </matrix>" +
+				"</template>";
+			MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
+			Assert.assertNotNull("template", template);
+	}
+	
+	@Test
+	public void testFields() {
+		String templateS = 
+				"<template>" +
+				"  <matrix>" +
+				"    <record id='r1' repeat='*' matrixFields='header'/>" +
+				"    <record id='r1' repeat='*' matrixFields='body'/>" +
+				"    <record id='r1' repeat='*' matrixFields='left body right'/>" +
+				"    <record id='r1' repeat='*' matrixFields='footer'/>" +
+				"  </matrix>" +
+				"</template>";
+			MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
+			Assert.assertNotNull("template", template);
+	}
+	
+	@Test
+	public void testBadFields() {
+		String templateS = 
+				"<template>" +
+				"  <matrix>" +
+				"    <record id='r1' repeat='*' matrixFields='heder'/>" +
+				"  </matrix>" +
+				"</template>";
+		try {
+			MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
+			Assert.assertTrue("must throw exception", true);
+		} catch (Exception e) {
+			
+		}
+	}
+	
+	@Test
+	public void testBody() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r1' repeat='*' matrixFields='body'>\\s*{2F,x:x}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+				" 1.1 1.2\n" +
+				" 2.1 2.1\n" +
+				" 3.1 3.2\n";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:role='matrixContainer'>" +
+				"    <list cmlx:role='body'>" +
+				"      <matrix rows='3' columns='2' dataType='xsd:double' dictRef='x:x'>1.1 1.2 2.1 2.1 3.1 3.2</matrix>" +
+				"    </list>" +
+				"  </list>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+	}
+	
+	@Test
+	public void testHeader() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r1' repeat='1' matrixFields='header'>\\s*{2I,x:col}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+				"  1   2 \n" +
+				"";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:role='matrixContainer'>" +
+				"	 <list cmlx:role='header'>" +
+				"	   <array dataType='xsd:integer' size='2' dictRef='x:col'>1 2</array>" +
+				"	 </list>" +
+				"  </list>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+	}
+	
+	
+	@Test
+	public void testDoubleHeaderWithoutMatrix() {
+		String templateS = 
+				"<template>" +
+				"    <record id='r1' repeat='1' matrixFields='header'>\\s*{2I,x:col}\\s*</record>" +
+				"    <record id='r2' repeat='1' matrixFields='header'>\\s*{2I,x:zzz}\\s*</record>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+					"  1   2 \n" +
+					"  99 100 \n" +
+				"";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='NULL_ID' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:templateRef='r1'>" +
+				"    <array dataType='xsd:integer' size='2' dictRef='x:col'>1 2</array>" +
+				"  </list>" +
+				"  <list cmlx:templateRef='r2'>" +
+				"    <array dataType='xsd:integer' size='2' dictRef='x:zzz'>99 100</array>" +
+				"  </list>" +
+				"</module>" +
+				"";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+	}
+	
+	@Test
+	public void testDoubleHeader() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r1' repeat='1' matrixFields='header'>\\s*{2I,x:head1}\\s*</record>" +
+				"    <record id='r2' repeat='1' matrixFields='header'>\\s*{2I,x:head2}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+					"  1   2 \n" +
+					"  99 100 \n" +
+				"";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:role='matrixContainer'>" +
+				"    <list cmlx:role='header'>" +
+				"      <array dataType='xsd:integer' size='2' dictRef='x:head1'>1 2</array>" +
+				"      <array dataType='xsd:integer' size='2' dictRef='x:head2'>99 100</array>" +
+				"    </list>" +
+				"  </list>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+	}
+	
+	public void testHeaderBody() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r1' repeat='1' matrixFields='header'>\\s*{2I,x:col}\\s*</record>" +
+				"    <record id='r2' repeat='*' matrixFields='body'>\\s*{2F,x:x}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+				"  1   2 \n" +
+				" 1.1 1.2\n" +
+				" 2.1 2.1\n" +
+				" 3.1 3.2\n" +
+				"";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <matrix rows='3' columns='2' dataType='xsd:double'>1.1 1.2 2.1 2.1 3.1 3.2</matrix>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+	}
+	
+	
+	@Test
+	public void testFooter0() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r4' repeat='1' matrixFields='footer'>\\s*{X,x:footer}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+				" A footer\n";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:role='matrixContainer'>" +
+				"    <list cmlx:role='footer'>" +
+				"      <scalar dataType='xsd:string' dictRef='x:footer'>A footer</scalar>" +
+				"    </list>" +
+				"  </list>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+			
+	}
+	
+	@Test
+	public void testFooter() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r3' repeat='*' matrixFields='body'>\\s*{2F,x:x}\\s*</record>" +
+				"    <record id='r4' repeat='1' matrixFields='footer'>\\s*{X,x:footer}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+				" 1.1 1.2\n" +
+				" 2.1 2.1\n" +
+				" 3.1 3.2\n" +
+				" A footer\n";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:role='matrixContainer'>" +
+				"    <matrix rows='3' columns='2' dataType='xsd:double'>1.1 1.2 2.1 2.1 3.1 3.2</matrix>" +
+				"    <list cmlx:role='footer'>" +
+				"      <scalar dataType='xsd:string' dictRef='x:footer'>A footer</scalar>" +
+				"    </list>" +
+				"  </list>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+			
+	}
+	
+	
+	@Test
+	public void testComplete() {
+		String templateS = 
+				"<template>" +
+				"  <matrix id='m1'>" +
+				"    <record id='r1' repeat='1' matrixFields='header'>\\s*{2I,x:head1}\\s*</record>" +
+				"    <record id='r2' repeat='1' matrixFields='header'>\\s*{2A,x:head2}\\s*</record>" +
+				"    <record id='r3' repeat='*' matrixFields='body'>\\s*{2F,x:x}\\s*</record>" +
+				"    <record id='r4' repeat='1' matrixFields='footer'>\\s*{X,x:footer}\\s*</record>" +
+				"  </matrix>" +
+				"</template>";
+			Template template = new Template(CMLUtil.parseXML(templateS));
+			String toBeParsed = "" +
+				"  1   2 \n" +
+				"  A   B \n" +
+				" 1.1 1.2\n" +
+				" 2.1 2.1\n" +
+				" 3.1 3.2\n" +
+				" A footer\n";
+			template.applyMarkup(toBeParsed);
+			LineContainer lineContainer = template.getLineContainer();
+			Assert.assertNotNull(lineContainer);
+			String refS = "" +
+				"<module cmlx:templateRef='m1' xmlns='http://www.xml-cml.org/schema' xmlns:cmlx='http://www.xml-cml.org/schema/cmlx'>" +
+				"  <list cmlx:role='matrixContainer'>" +
+				"    <list cmlx:role='header'>" +
+				"      <array dataType='xsd:integer' dictRef='x:head1'>1 2</array>" +
+				"      <array dataType='xsd:string' dictRef='x:head2'>A B</array>" +
+				"    </list>" +
+				"    <matrix rows='3' columns='2' dataType='xsd:double'>1.1 1.2 2.1 2.1 3.1 3.2</matrix>" +
+				"    <list cmlx:role='footer'>" +
+				"      <scalar dataType='xsd:string' dictRef='x:footer'>A footer</scalar>" +
+				"    </list>" +
+				"  </list>" +
+				"</module>";
+			JumboTestUtils.assertEqualsCanonically("Row", refS, lineContainer.getLinesElement(), true);
+			
+	}
+}
+

src/test/java/org/xmlcml/cml/converters/templates/TemplateTest.java

 import org.xmlcml.cml.base.CMLUtil;
 import org.xmlcml.cml.converters.Outputter.OutputLevel;
 import org.xmlcml.cml.converters.templates.output.LineContainer;
+import org.xmlcml.cml.converters.templates.output.MarkupContainer;
 import org.xmlcml.cml.converters.templates.output.Template;
 import org.xmlcml.cml.testutil.JumboTestUtils;
 
 			"</template>";
 		try {
 			@SuppressWarnings("unused")
-			Template template = new Template(CMLUtil.parseXML(templateS));
+			MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
 			Assert.assertTrue("exception should not now be thrown", true);
 		} catch (Exception e) {
 			Assert.fail("exception should not be thrown "+ e);
 	public void testTemplateTemplate1() {
 		String templateS = 
 			"<template pattern='.*1.*' offset='0' id='t1' name='template 1' endPattern='.*3.*' endOffset='1'/>";
-		Template template = new Template(CMLUtil.parseXML(templateS));
+		MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
 		String s = template.toString();
 		Assert.assertEquals("template", "startChunker [.*1.*] | 0\nendChunker [.*3.*] | 1\n", s);
 	}
 	public void testTemplateTemplateDefaultEndOffset() {
 		String templateS = 
 			"<template pattern='.*1.*' offset='2' id='t1' name='template 1' endPattern='.*3.*'/>";
-		Template template = new Template(CMLUtil.parseXML(templateS));
+		MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
 		String s = template.toString();
 		Assert.assertEquals("template", "startChunker [.*1.*] | 2\nendChunker [.*3.*] | 0\n", s);
 	}
 	public void testTemplateTemplateDefaultEndPattern() {
 		String templateS = 
 			"<template pattern='.*1.*' offset='2' id='t1' name='template 1' />";
-		Template template = new Template(CMLUtil.parseXML(templateS));
+		MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
 		String s = template.toString();
 		Assert.assertEquals("template", "startChunker [.*1.*] | 2\nendChunker [~] | 0\n", s);
 	}
 	public void testTemplateTemplateDefaultOffset() {
 		String templateS = 
 			"<template pattern='.*1.*' id='t1' name='template 1' />";
-		Template template = new Template(CMLUtil.parseXML(templateS));
+		MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
 		String s = template.toString();
 		Assert.assertEquals("template", "startChunker [.*1.*] | 0\nendChunker [~] | 0\n", s);
 	}
 	public void testTemplateTemplateDefaultOffset1() {
 		String templateS = 
 			"<template pattern='.*1.*' id='t1' name='template 1' endOffset='1'/>";
-		Template template = new Template(CMLUtil.parseXML(templateS));
+		MarkupContainer template = new Template(CMLUtil.parseXML(templateS));
 		String s = template.toString();
 		Assert.assertEquals("template", "startChunker [.*1.*] | 0\nendChunker [~] | 1\n", s);
 	}

target/classes/META-INF/MANIFEST.MF

+Manifest-Version: 1.0
+Built-By: pm286
+Build-Jdk: 1.7.0_02
+Created-By: Maven Integration for Eclipse
+

target/classes/META-INF/maven/org.xml-cml.jc/jc-template-parser/pom.properties

+#Generated by Maven Integration for Eclipse
+#Tue Mar 06 11:28:14 GMT 2012
+version=0.3-SNAPSHOT
+groupId=org.xml-cml.jc
+m2e.projectName=jc-template-parser
+m2e.projectLocation=D\:\\petermr-workspace\\jumbo-converters\\jc-template-parser
+artifactId=jc-template-parser

target/classes/META-INF/maven/org.xml-cml.jc/jc-template-parser/pom.xml

+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.xml-cml.jc</groupId>
+        <artifactId>jc-top</artifactId>
+        <version>0.3-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>jc-template-parser</artifactId>
+    <name>jc-template-parser</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>${jc.groupId}</groupId>
+            <artifactId>jc-top-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>${jumbo.groupId}</groupId>
+            <artifactId>jumbo-testutil</artifactId>
+            <version>${jumbo-testutil.version}</version>
+        </dependency>
+        
+    </dependencies>
+</project>
Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/TemplateTestUtils.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/TemplateTester.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/input/FreemarkerTextConverter.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/input/InputGenerator.class

Binary file added.

target/classes/org/xmlcml/cml/converters/templates/input/text.dtd

+<!ENTITY % formatType  'formatType      (FORTRAN|WHITESPACE|REGEX)'>
+<!ENTITY % id          'id              CDATA'>
+<!ENTITY % linesToRead 'linesToRead     CDATA'>
+<!ENTITY % makeArray   'makeArray       CDATA'>
+<!ENTITY % multiple    'multiple        CDATA'>
+<!ENTITY % name        'name            CDATA'>
+<!ENTITY % names       'names           CDATA'>
+<!ENTITY % offset      'pattern         CDATA'>
+<!ENTITY % output      'output          (VERBOSE|NORMAL|NONE)'>
+<!ENTITY % pattern     'pattern         CDATA'>
+<!ENTITY % prefix      'prefix          CDATA'>
+<!ENTITY % repeat      'repeat          (WHILE|UNTIL)'>
+<!ENTITY % role        'role            (EXAMPLE|DESCRIPTION)'>
+<!ENTITY % uri         'uri             CDATA'>
+
+<!ELEMENT templateList (template*)>
+<!ATTLIST templateList
+    >
+<!ELEMENT template 
+    (dictionary*,deleter*,templateList?,(comment|readLines|record))*>
+<!ATTLIST template
+    %id;            #REQUIRED
+    %name;          #REQUIRED
+    %output;        #IMPLIED
+    %pattern;       #REQUIRED
+    >
+
+<!--  <deleter multiple="\$" pattern=".*KE Trans.*$\s*Translational\s*$.*KE Trans.*"/>-->
+<!ELEMENT deleter EMPTY>
+<!ATTLIST deleter
+    %multiple;      #IMPLIED
+    %offset;        #IMPLIED
+    %pattern;       #REQUIRED
+    >
+
+<!--  <dictionary id="i1" uri="http://www.xml-cml.org/dictionary/foo" prefix="cml"/>-->
+<!ELEMENT dictionary EMPTY>
+<!ATTLIST dictionary
+    %id;            #REQUIRED
+    %uri;           #REQUIRED
+    %prefix;        #REQUIRED
+    >
+
+<!ELEMENT readLines (#PCDATA)>
+<!ATTLIST readLines
+    %formatType;    #IMPLIED
+    %id;            #IMPLIED
+    %linesToRead;   #IMPLIED
+    %output;        #IMPLIED
+    %repeat;        #REQUIRED
+    >
+
+<!ELEMENT record   (#PCDATA)>
+<!ATTLIST record
+    %formatType;    #REQUIRED
+    %id;            #IMPLIED
+    %linesToRead;   #IMPLIED
+    %makeArray;     #IMPLIED
+    %names;         #IMPLIED
+    %output;        #IMPLIED
+    %repeat;        #IMPLIED
+    >
+    
+<!ELEMENT comment (#PCDATA)>
+<!ATTLIST comment
+    %role;          #IMPLIED
+    >
+    

target/classes/org/xmlcml/cml/converters/templates/input/topTemplate.xsl

+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<xsl:output method="html"/>
+
+<xsl:template match="/">
+  <html>
+    <head>
+      <style type="text/css">
+      .comment {
+        background-color: #eeee77;
+      }
+  </style>
+    </head>
+    <xsl:apply-templates select=".//template"/>
+  </html>
+</xsl:template>
+
+<xsl:template match="template">
+  <div>
+    <h1 class="title"><i><xsl:value-of select="@id"/></i>: <xsl:value-of select="@name"/></h1>
+  </div>
+  <table border="1">
+    <tr><td>repeat</td><td><xsl:value-of select="@repeat | @repeatCount"/></td></tr>
+    <tr><td>pattern</td><td><xsl:value-of select="@pattern"/></td></tr>
+    <tr><td>offset</td><td><xsl:value-of select="@offset"/></td></tr>
+    <tr><td>newline</td><td><xsl:value-of select="@multiple | @newline"/></td></tr>
+    <tr><td>endPattern</td><td><xsl:value-of select="@endPattern"/></td></tr>
+    <tr><td>endOffset</td><td><xsl:value-of select="@endOffset"/></td></tr>
+  </table>
+  <xsl:for-each select="comment">
+    <div class="comment">
+      <pre>
+        <xsl:value-of select="."/>
+      </pre>
+    </div>
+  </xsl:for-each>
+  <xsl:apply-templates select="transform"/>
+</xsl:template>
+
+<xsl:template match="transform">
+  <table border="2">
+   <tr><td>ID</td><td><xsl:value-of select="@id"/></td></tr>
+   <tr><td>parent</td><td><xsl:value-of select="@parent"/></td></tr>
+   <tr><td>name/xpath</td><td><xsl:value-of select="@name"/></td></tr>
+  </table>
+</xsl:template>
+
+</xsl:stylesheet>
Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/AbstractTransformConverter.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/Chunker.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/Debug.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/Deleter.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/Examples.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/LineContainer.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/LineReader$FormatType.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/LineReader$LineType.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/LineReader$ReadingType.class

Binary file added.

Add a comment to this file

target/classes/org/xmlcml/cml/converters/templates/output/LineReader.class

Binary file added.