package folioxml.slx; import folioxml.core.InvalidMarkupException; import folioxml.css.CssClassCleaner; import folioxml.folio.FolioTokenReader; import folioxml.translation.SlxTranslatingReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; /** * Reads and transforms a stream of compatibilty slx into valid slx. * Auto-creates a root record to contain all incoming tokens. * <p> * Call .saveCssTable() after completion so that transformation back to FFF is possible. * * @author nathanael */ public class SlxRecordReader { public SlxRecord getRootRecord() { return root; } public SlxRecordReader(ISlxTokenReader stream, ISlxTokenWriter postTransformFilter) throws InvalidMarkupException { this.stream = stream; this.defaultFilter = postTransformFilter; root = nextRecordTag = new SlxRecord("<record level=\"root\">"); cssCleaner = new CssClassCleaner(); objectResolver = new ObjectResolver(root); } public SlxRecordReader(ISlxTokenReader stream) throws InvalidMarkupException { this(stream, null); } /** * Reads a folio flat file into an SlxRecord stream * * @param f * @throws InvalidMarkupException * @throws UnsupportedEncodingException * @throws FileNotFoundException * @throws IOException */ public SlxRecordReader(File f) throws InvalidMarkupException, UnsupportedEncodingException, FileNotFoundException, IOException { this(new SlxTranslatingReader(new FolioTokenReader(f)), null); } private ISlxTokenReader stream = null; private ISlxTokenWriter defaultFilter = null; private SlxRecord root = null; private CssClassCleaner cssCleaner = null; private ObjectResolver objectResolver = null; /** * Points to the last parsed record that has a level indicator. This can be used to determine the next record's parent. */ private SlxRecord lastLevelRecord = null; /** * Points to the record token that was encountered on the last read(), and terminated it. */ private SlxRecord nextRecordTag = null; public boolean silent; /** * Returns a reference to the CssClassCleaner in use. * * @return */ public CssClassCleaner getCssCleaner() { return cssCleaner; } public ObjectResolver getObjectResolver() { return objectResolver; } /** * Saves the CSS mapping to the root. * @throws InvalidMarkupException */ //public void saveCssTable() throws InvalidMarkupException{ // cssCleaner.saveTo(root); //} /** * Returns the next record. The first call to read() will return the root record, which contains the infobase-level data and is parent to all records. * * @return * @throws java.io.IOException */ public SlxRecord read() throws IOException, InvalidMarkupException { return read(null); } public void close() throws IOException { stream.close(); stream = null; } /** * Returns the next record. The first call to read() will return the root record, which contains the infobase-level data and is parent to all records. * * @return * @throws java.io.IOException */ public SlxRecord read(ISlxTokenWriter postTransformFilter) throws IOException, InvalidMarkupException { SlxRecord r = nextRecordTag; nextRecordTag = null; if (r == null) return null; //No record tag was found last read(), or no root node was defined in the constructor //Transforms the 'translated' Slx token stream into a valid Slx token stream. There isn't a 1-1 correlation, so it may write any number of tokens into //the specified record when .write(SlxToken) is called. Or it may write none, and simply modify previous tokens. if (postTransformFilter == null) { if (defaultFilter != null) { postTransformFilter = defaultFilter; } else { postTransformFilter = r; } } postTransformFilter.setUnderlyingWriter(r); //Transformer SlxTransformer transformer = new SlxTransformer(postTransformFilter, r); //Read folio tokens one-by-one while (stream.canRead()) { SlxToken ft = stream.read(); //Stop when we hit the next record if (ft != null && ft.matches("record")) { //Store for next read() call this.nextRecordTag = new SlxRecord(ft); break; } if (ft != null) transformer.write(ft); } //Fix identifiers such as class, jumpdestination, and name. Note! This cannot be done during transformation, since <p> tags only receive their class attribute after they are already written. //Css cleaning layer. //Resolve object tags if (r == root) { cssCleaner.processRootRecord(r); } else { //Fixed indexing bug Feb. 2. Running cssCleaner twice on the def was corrupting the tables. cssCleaner.process(r); for (SlxToken t : r.getTokens()) { cssCleaner.process(t); //Fix CSS classes objectResolver.fixToken(t); //Fix object tags - resolve using infobase definition. } } //Set default 'class' if (r.get("class") == null) r.set("class", "NormalLevel"); //LV (level) tags can appear anywhere in the record, so calculateParent() must be called after the record has been parsed through the FolioSlxTransformer r.calculateParent(lastLevelRecord, true); r.ghostPairsGenerated = true; //Store last level record for heirarchy calculations if (r.isLevelRecord()) lastLevelRecord = r; //HACK - doesn't handled undefined styles... Re-asses this. if ("root".equalsIgnoreCase(r.get("level"))) { cssCleaner.saveTo(r); } //Must be called after cssCleaner.saveTo(), since the mapping tags cannot be written outside the record node. transformer.endRecord(true); transformer.verifyDone(); return r; } }