/** * Copyright 2009, Google Inc. All rights reserved. * Licensed to PSF under a Contributor Agreement. */ package org.python.indexer.demos; import org.python.indexer.StyleRun; import java.util.ArrayList; import java.util.List; import java.util.HashSet; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; /** * Turns a list of {@link StyleRun}s into HTML spans. */ class StyleApplier { // Empirically, adding the span tags multiplies length by 6 or more. private static final int SOURCE_BUF_MULTIPLIER = 6; private SortedSet<Tag> tags = new TreeSet<Tag>(); private StringBuilder buffer; // html output buffer private String source; // input source code private String path; // input source path, for logging // Current offset into the source being copied into the html buffer. private int sourceOffset = 0; abstract class Tag implements Comparable<Tag>{ int offset; StyleRun style; public int compareTo(Tag other) { if (this == other) { return 0; } if (this.offset < other.offset) { return -1; } if (other.offset < this.offset) { return 1; } return this.hashCode() - other.hashCode(); } void insert() { // Copy source code up through this tag. if (offset > sourceOffset) { copySource(sourceOffset, offset); } } } class StartTag extends Tag { public StartTag(StyleRun style) { offset = style.start(); this.style = style; } @Override void insert() { super.insert(); switch (style.type) { case ANCHOR: buffer.append("<a name='"); buffer.append(style.url); break; case LINK: buffer.append("<a href='"); buffer.append(style.url); break; default: buffer.append("<span class='"); buffer.append(toCSS(style)); break; } buffer.append("'"); if (style.message != null) { buffer.append(" title='"); buffer.append(style.message); buffer.append("'"); } buffer.append(">"); } } class EndTag extends Tag { public EndTag(StyleRun style) { offset = style.end(); this.style = style; } @Override void insert() { super.insert(); switch (style.type) { case ANCHOR: case LINK: buffer.append("</a>"); break; default: buffer.append("</span>"); break; } } } public StyleApplier(String path, String src, List<StyleRun> runs) { this.path = path; source = src; for (StyleRun run : runs) { tags.add(new StartTag(run)); tags.add(new EndTag(run)); } } /** * @return the html */ public String apply() { buffer = new StringBuilder(source.length() * SOURCE_BUF_MULTIPLIER); int lastStart = -1; for (Tag tag : tags) { tag.insert(); } // Copy in remaining source beyond last tag. if (sourceOffset < source.length()) { copySource(sourceOffset, source.length()); } return buffer.toString(); } /** * Copies code from the input source to the output html. * @param beg the starting source offset * @param end the end offset, or -1 to go to end of file */ private void copySource(int beg, int end) { // Be robust if the analyzer gives us bad offsets. try { String src = escape((end == -1) ? source.substring(beg) : source.substring(beg, end)); buffer.append(src); } catch (RuntimeException x) { System.err.println("Warning: " + x); } sourceOffset = end; } private String escape(String s) { return s.replace("&", "&") .replace("'", "'") .replace("\"", """) .replace("<", "<") .replace(">", ">"); } private String toCSS(StyleRun style) { return style.type.toString().toLowerCase().replace("_", "-"); } }