1 16 package org.outerj.daisy.books.publisher.impl.publicationprocess; 17 18 import org.xml.sax.ContentHandler ; 19 import org.xml.sax.Attributes ; 20 import org.xml.sax.SAXException ; 21 import org.xml.sax.InputSource ; 22 import org.outerj.daisy.books.publisher.impl.util.AbstractContentHandler; 23 import org.outerj.daisy.books.publisher.impl.BookInstanceLayout; 24 import org.outerj.daisy.books.store.BookInstance; 25 import org.outerj.daisy.xmlutil.XmlSerializer; 26 import org.outerj.daisy.xmlutil.LocalSAXParserFactory; 27 import org.apache.cocoon.xml.AttributesImpl; 28 import org.apache.cocoon.transformation.I18nTransformer; 29 30 import javax.xml.parsers.SAXParser ; 31 import java.util.*; 32 import java.util.regex.Pattern ; 33 import java.util.regex.Matcher ; 34 import java.text.Collator ; 35 import java.io.InputStream ; 36 import java.io.OutputStream ; 37 38 public class AddIndexTask implements PublicationProcessTask { 39 private final String input; 40 private final String output; 41 42 public AddIndexTask(String input, String output) { 43 this.input = input; 44 this.output = output; 45 } 46 47 public void run(PublicationContext context) throws Exception { 48 context.getPublicationLog().info("Running add index task."); 49 BookInstance bookInstance = context.getBookInstance(); 50 String publicationOutputPath = BookInstanceLayout.getPublicationOutputPath(context.getPublicationOutputName()); 51 InputStream is = null; 52 OutputStream os = null; 53 try { 54 is = bookInstance.getResource(publicationOutputPath + input); 55 os = bookInstance.getResourceOutputStream(publicationOutputPath + output); 56 SAXParser parser = LocalSAXParserFactory.getSAXParserFactory().newSAXParser(); 57 XmlSerializer serializer = new XmlSerializer(os); 58 AddIndexHandler addIndexHandler = new AddIndexHandler(serializer, context.getLocale()); 59 parser.getXMLReader().setContentHandler(addIndexHandler); 60 parser.getXMLReader().parse(new InputSource (is)); 61 } finally { 62 if (is != null) 63 try { is.close(); } catch (Exception e) { 64 context.getPublicationLog().error("Error closing input stream.", e); 65 } 66 if (os != null) 67 try { os.close(); } catch (Exception e) { 68 context.getPublicationLog().error("Error closing output stream.", e); 69 } 70 } 71 } 72 73 static class AddIndexHandler extends AbstractContentHandler { 74 private Index index = new Index(); 75 private StringBuffer indexEntryBuffer = new StringBuffer (); 76 private int indexEntryNesting = -1; 77 private String indexEntryId = null; 78 private int nesting = 0; 79 private int indexNameCounter = 1; 80 private Locale locale; 81 82 public AddIndexHandler(ContentHandler consumer, Locale locale) { 83 super(consumer); 84 this.locale = locale; 85 } 86 87 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 88 nesting++; 89 if (indexEntryNesting == -1 && namespaceURI.equals("") && localName.equals("span")) { 91 String className = atts.getValue("class"); 92 if ("indexentry".equals(className)) { 93 indexEntryBuffer.setLength(0); 94 indexEntryNesting = nesting; 95 96 String id = atts.getValue("id"); 97 if (id == null) { 98 id = "dsy_idx_" + indexNameCounter++; 99 AttributesImpl newAttrs = new AttributesImpl(atts); 100 newAttrs.addCDATAAttribute("id", id); 101 atts = newAttrs; 102 } 103 indexEntryId = id; 104 } 105 } 106 super.startElement(namespaceURI, localName, qName, atts); 107 } 108 109 public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 110 if (indexEntryNesting == nesting) { 111 String entry = indexEntryBuffer.toString(); 113 IndexEntry indexEntry = index.getEntry(entry); 114 if (indexEntry != null) 115 indexEntry.addId(indexEntryId); 116 117 indexEntryNesting = -1; 119 indexEntryId = null; 120 } else if (nesting == 2 && namespaceURI.equals("") && localName.equals("body")) { 121 index.generateSaxFragment(consumer, locale); 123 } 124 nesting--; 125 super.endElement(namespaceURI, localName, qName); 126 } 127 128 public void characters(char ch[], int start, int length) throws SAXException { 129 if (indexEntryNesting != -1) 130 indexEntryBuffer.append(ch, start, length); 131 super.characters(ch, start, length); 132 } 133 } 134 135 static class Index { 136 private Map entries = new HashMap(); 137 138 public IndexEntry getEntry(String name) { 139 Pattern subentryPattern = Pattern.compile("([^:]+)"); 140 Matcher subentryMatcher = subentryPattern.matcher(name); 141 IndexEntry indexEntry = null; 142 while (subentryMatcher.find()) { 143 String subentry = subentryMatcher.group(1).trim(); 144 if (subentry.length() == 0) 145 continue; 146 if (indexEntry == null) { 147 indexEntry = (IndexEntry)entries.get(subentry); 148 if (indexEntry == null) { 149 indexEntry = new IndexEntry(subentry); 150 entries.put(subentry, indexEntry); 151 } 152 } else { 153 indexEntry = indexEntry.getSubEntry(subentry); 154 } 155 } 156 return indexEntry; 157 } 158 159 public void generateSaxFragment(ContentHandler contentHandler, Locale locale) throws SAXException { 160 if (entries.size() == 0) 161 return; 162 163 contentHandler.characters(new char[] {'\n'}, 0, 1); 164 AttributesImpl indexHeaderAttrs = new AttributesImpl(); 165 indexHeaderAttrs.addCDATAAttribute("id", "index"); 166 contentHandler.startElement("", "h1", "h1", indexHeaderAttrs); 167 168 AttributesImpl indexTitleAttrs = new AttributesImpl(); 169 indexTitleAttrs.addCDATAAttribute("key", "index"); 170 contentHandler.startPrefixMapping("i18n", I18nTransformer.I18N_NAMESPACE_URI); 171 contentHandler.startElement(I18nTransformer.I18N_NAMESPACE_URI, "text", "i18n:text", indexTitleAttrs); 172 contentHandler.endElement(I18nTransformer.I18N_NAMESPACE_URI, "text", "i18n:text"); 173 contentHandler.endPrefixMapping("i18n"); 174 175 contentHandler.endElement("", "h1", "h1"); 176 177 contentHandler.characters(new char[] {'\n'}, 0, 1); 178 contentHandler.startElement("", "index", "index", new AttributesImpl()); 179 180 IndexEntry[] entries = (IndexEntry[])this.entries.values().toArray(new IndexEntry[0]); 181 IndexEntryComparator comparator = new IndexEntryComparator(locale); 182 Arrays.sort(entries, comparator); 183 184 IndexEntry prevEntry = null; 185 for (int i = 0; i < entries.length; i++) { 186 if (prevEntry == null || isDifferentGroup(prevEntry, entries[i])) { 187 if (prevEntry != null) { 188 contentHandler.characters(new char[] {'\n'}, 0, 1); 189 contentHandler.endElement("", "indexGroup", "indexGroup"); 190 } 191 AttributesImpl indexGroupAtrrs = new AttributesImpl(); 192 if (Character.isLetter(entries[i].getName().charAt(0))) 193 indexGroupAtrrs.addCDATAAttribute("name", String.valueOf(Character.toUpperCase(entries[i].getName().charAt(0)))); 194 contentHandler.characters(new char[] {'\n'}, 0, 1); 195 contentHandler.startElement("", "indexGroup", "indexGroup", indexGroupAtrrs); 196 } 197 entries[i].generateSaxFragment(contentHandler, comparator); 198 prevEntry = entries[i]; 199 } 200 201 if (prevEntry != null) { 202 contentHandler.characters(new char[] {'\n'}, 0, 1); 203 contentHandler.endElement("", "indexGroup", "indexGroup"); 204 } 205 206 contentHandler.characters(new char[] {'\n'}, 0, 1); 207 contentHandler.endElement("", "index", "index"); 208 } 209 210 public boolean isDifferentGroup(IndexEntry entry1, IndexEntry entry2) { 211 char c1 = Character.toUpperCase(entry1.getName().charAt(0)); 212 char c2 = Character.toUpperCase(entry2.getName().charAt(0)); 213 return c1 != c2 && Character.isLetter(c2); 214 } 215 } 216 217 static class IndexEntry { 218 private String name; 219 private List ids = new ArrayList(); 220 private Map children; 221 222 public IndexEntry(String name) { 223 this.name = name; 224 } 225 226 public void addId(String id) { 227 this.ids.add(id); 228 } 229 230 public String getName() { 231 return name; 232 } 233 234 public void addChild(IndexEntry entry) { 235 if (children == null) 236 children = new HashMap(); 237 children.put(entry.getName(), entry); 238 } 239 240 public IndexEntry getSubEntry(String name) { 241 IndexEntry indexEntry = children == null ? null : (IndexEntry)children.get(name); 242 if (indexEntry == null) { 243 indexEntry = new IndexEntry(name); 244 addChild(indexEntry); 245 } 246 return indexEntry; 247 } 248 249 public void generateSaxFragment(ContentHandler contentHandler, IndexEntryComparator comparator) throws SAXException { 250 AttributesImpl entryAttrs = new AttributesImpl(); 251 entryAttrs.addAttribute("", "name", "name", "CDATA", getName()); 252 contentHandler.characters(new char[] {'\n'}, 0, 1); 253 contentHandler.startElement("", "indexEntry", "indexEntry", entryAttrs); 254 Iterator entriesIt = ids.iterator(); 255 while (entriesIt.hasNext()) { 256 String id = (String )entriesIt.next(); 257 contentHandler.characters(new char[] {'\n'}, 0, 1); 258 contentHandler.startElement("", "id", "id", new AttributesImpl()); 259 contentHandler.characters(id.toCharArray(), 0, id.length()); 260 contentHandler.endElement("", "id", "id"); 261 } 262 if (children != null) { 263 IndexEntry[] entries = (IndexEntry[])children.values().toArray(new IndexEntry[0]); 264 Arrays.sort(entries, comparator); 265 for (int i = 0; i < entries.length; i++) { 266 entries[i].generateSaxFragment(contentHandler, comparator); 267 } 268 } 269 contentHandler.characters(new char[] {'\n'}, 0, 1); 270 contentHandler.endElement("", "indexEntry", "indexEntry"); 271 } 272 } 273 274 static class IndexEntryComparator implements Comparator { 275 private final Collator collator; 276 277 public IndexEntryComparator(Locale locale) { 278 this.collator = Collator.getInstance(locale); 279 } 280 281 public int compare(Object o1, Object o2) { 282 String entry1 = ((IndexEntry)o1).getName(); 283 String entry2 = ((IndexEntry)o2).getName(); 284 return collator.compare(entry1, entry2); 285 } 286 } 287 } 288 | Popular Tags |