001: package gnu.kawa.util;
002:
003: import gnu.mapping.*;
004: import java.io.*;
005: import java.net.*;
006: import java.util.*;
007: import java.util.regex.*;
008:
009: /** A utility class for updating web pages.
010: * This is used for the Kawa/Qexo web pages.
011: * It assumes an input html file which looks like:
012: * <pre>
013: * <i>... copied as is ...</i>
014: * <!--start-generated-navbar-->
015: * <i>... ignored ...</i>
016: * <!--start-children-toc-->
017: * <ul [parent="parent-link.html"]>
018: * <li><a href="...">...</a></li>
019: * </ul>
020: * <!--end-children-toc-->
021: * <i>... ignored ...</i>
022: * <!--end-generated-navbar-->
023: * <i>... copied as is ...</i>
024: * </pre>
025: *
026: * The file is updated to have the same format, but merging in
027: * navigation bar links from the {@code "parent-link.html"}.
028: */
029:
030: public class FixupHtmlToc {
031: static FileInfo[] argFiles;
032:
033: public static void main(String[] args) {
034: try {
035: argFiles = new FileInfo[args.length];
036: for (int i = 0; i < args.length; i++) {
037: FileInfo info = FileInfo.find(new File(args[i]));
038: info.writeNeeded = true;
039: argFiles[i] = info;
040: }
041:
042: for (int i = 0; i < args.length; i++) {
043: argFiles[i].scan();
044: argFiles[i].write();
045: }
046: } catch (Throwable ex) {
047: System.err.println("caught " + ex);
048: ex.printStackTrace();
049: }
050: }
051: }
052:
053: class FileInfo {
054: static Hashtable fileMap = new Hashtable();
055:
056: boolean writeNeeded;
057: /** Path relative to user.dir */
058: String path;
059: File file;
060: FileInfo parent;
061: String parentName;
062: String[] childLinkText;
063: int nchildren;
064: FileInputStream fin;
065: InPort in;
066: ByteArrayOutputStream bout;
067: OutPort cout;
068: boolean scanned;
069:
070: StringBuffer beforeNavbarText;
071: StringBuffer oldNavbarText;
072: StringBuffer newNavbarText;
073:
074: public static FileInfo find(File file) throws Throwable {
075: String abs = file.getCanonicalPath();
076: FileInfo info = (FileInfo) fileMap.get(abs);
077: if (info == null) {
078: info = new FileInfo();
079: info.file = file;
080: fileMap.put(abs, info);
081: }
082: return info;
083: }
084:
085: static final Pattern childPat = Pattern.compile("<a .*</a>");
086: static final Pattern parentPat = Pattern
087: .compile("<ul[^>]* parent=['\"]([^'\"]*)['\"]");
088: static final Pattern linkPat = Pattern
089: .compile(" href=['\"]([^'\"]*)['\"]");
090:
091: public void scan() throws Throwable {
092: if (scanned)
093: return;
094: scanned = true;
095: fin = new FileInputStream(file);
096: in = new InPort(new BufferedInputStream(fin));
097: oldNavbarText = new StringBuffer();
098: newNavbarText = new StringBuffer();
099: if (writeNeeded) {
100: bout = new ByteArrayOutputStream();
101: cout = new OutPort(bout);
102: }
103: int lineno = 0;
104: boolean inNavbar = false;
105: boolean inChildList = false;
106: Vector chvec = new Vector();
107: for (;;) {
108: String line = in.readLine();
109: if (line == null)
110: break;
111: if (inNavbar) {
112: if (line.indexOf("<!--end-generated-navbar-->") >= 0) {
113: inNavbar = false;
114: break;
115: }
116: oldNavbarText.append(line);
117: oldNavbarText.append('\n');
118: if (inChildList) {
119: if (line.indexOf("<!--end-children-toc-->") >= 0)
120: inChildList = false;
121: else {
122: Matcher childMatcher = childPat.matcher(line);
123: if (childMatcher.find()) {
124: chvec.add(childMatcher.group());
125: }
126: Matcher parentMatcher = parentPat.matcher(line);
127: if (parentMatcher.find() && parentName == null) {
128: parentName = parentMatcher.group(1);
129: }
130: }
131: }
132: if (line.indexOf("<!--start-children-toc-->") >= 0)
133: inChildList = true;
134: } else {
135: if (line.indexOf("<!--start-generated-navbar-->") >= 0) {
136: inNavbar = true;
137: }
138: }
139: if (writeNeeded && !inNavbar)
140: cout.println(line);
141: }
142: String[] charr = new String[chvec.size()];
143: nchildren = charr.length;
144: chvec.copyInto(charr);
145: childLinkText = charr;
146: if (!writeNeeded)
147: in.close();
148: if (parentName != null) {
149: File parentFile = new File(file.toURI().resolve(parentName));
150: FileInfo parentInfo = FileInfo.find(parentFile);
151: parentInfo.scan();
152: this .parent = parentInfo;
153: }
154: }
155:
156: public void writeLinks(int uplevel, StringBuffer out)
157: throws Throwable {
158: FileInfo current = this ;
159: FileInfo this child = null;
160: for (int i = uplevel; --i >= 0;) {
161: this child = current;
162: current = current.parent;
163: }
164: if (uplevel == 0)
165: out.append("<!--start-children-toc-->\n");
166: if (uplevel == 0 && parentName != null) {
167: out.append("<ul parent=\"");
168: out.append(parentName);
169: out.append("\">\n");
170: } else
171: out.append("<ul>\n");
172: URI this URI = this .file.toURI();
173: URI currentURI = current.file.toURI();
174: for (int i = 0; i < current.nchildren; i++) {
175: String linkText = current.childLinkText[i];
176: boolean ancestor = false;
177: //System.err.println("linkText:"+linkText);
178: if (uplevel > 0) {
179: Matcher linkMatcher = linkPat.matcher(linkText);
180: linkMatcher.find();
181: String hrefText = linkMatcher.group(1);
182: URI linkURI = currentURI.resolve(hrefText);
183: linkText = linkMatcher.replaceFirst(" href=\""
184: + gnu.text.Path.relativize(linkURI.toString(),
185: this URI.toString()) + "\"");
186: int hash = hrefText.indexOf('#');
187: if (hash >= 0)
188: hrefText = hrefText.substring(0, hash);
189: FileInfo curchild = FileInfo.find(new File(currentURI
190: .resolve(hrefText)));
191: ancestor = curchild == this child;
192: //System.err.println(" ancestor:"+ancestor);
193: if (!ancestor && uplevel > 1)
194: continue;
195: }
196: out.append("<li>");
197: out.append(linkText);
198: if (ancestor) {
199: out.append('\n');
200: writeLinks(uplevel - 1, out);
201: }
202: out.append("</li>\n");
203: }
204: out.append("</ul>\n");
205: if (uplevel == 0)
206: out.append("<!--end-children-toc-->\n");
207: }
208:
209: public void write() throws Throwable {
210: int level = 0;
211: FileInfo current = this ;
212: while (current.parent != null) {
213: current = current.parent;
214: level++;
215: }
216: cout.println("<!--start-generated-navbar-->");
217: writeLinks(level, newNavbarText);
218: cout.print(newNavbarText);
219: cout.println("<!--end-generated-navbar-->");
220: for (;;) {
221: String line = in.readLine();
222: if (line == null)
223: break;
224: cout.println(line);
225: }
226: StringBuffer sbuf = new StringBuffer();
227: in.close();
228: if (oldNavbarText.toString().equals(newNavbarText.toString()))
229: System.err.println("fixup " + file + " - no change");
230: else {
231: FileOutputStream outs = new FileOutputStream(file);
232: byte[] bbuf = bout.toByteArray();
233: outs.write(bbuf);
234: outs.close();
235: System.err.println("fixup " + file + " - updated");
236: }
237: }
238: }
|