Util.java revision 1190
0N/A/*
0N/A * CDDL HEADER START
0N/A *
0N/A * The contents of this file are subject to the terms of the
407N/A * Common Development and Distribution License (the "License").
0N/A * You may not use this file except in compliance with the License.
0N/A *
0N/A * See LICENSE.txt included in this distribution for the specific
0N/A * language governing permissions and limitations under the License.
0N/A *
0N/A * When distributing Covered Code, include this CDDL HEADER in each
0N/A * file and include the License file at LICENSE.txt.
0N/A * If applicable, add the following below this CDDL HEADER, with the
0N/A * fields enclosed by brackets "[]" replaced with your own identifying
0N/A * information: Portions Copyright [yyyy] [name of copyright owner]
0N/A *
0N/A * CDDL HEADER END
0N/A */
0N/A/*
1124N/A * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
1185N/A * Portions Copyright 2011 Jens Elkner.
0N/A */
0N/Apackage org.opensolaris.opengrok.web;
0N/A
1185N/Aimport java.io.File;
1185N/Aimport java.io.FileInputStream;
1185N/Aimport java.io.FileReader;
154N/Aimport java.io.IOException;
1185N/Aimport java.io.InputStreamReader;
1185N/Aimport java.io.Reader;
159N/Aimport java.io.UnsupportedEncodingException;
154N/Aimport java.io.Writer;
292N/Aimport java.net.URI;
292N/Aimport java.net.URISyntaxException;
159N/Aimport java.net.URLEncoder;
154N/Aimport java.text.DecimalFormat;
154N/Aimport java.text.NumberFormat;
1124N/Aimport java.util.Collection;
1185N/Aimport java.util.LinkedList;
434N/Aimport java.util.logging.Level;
1185N/Aimport java.util.zip.GZIPInputStream;
1185N/A
1185N/Aimport org.opensolaris.opengrok.Info;
434N/Aimport org.opensolaris.opengrok.OpenGrokLogger;
259N/Aimport org.opensolaris.opengrok.configuration.RuntimeEnvironment;
89N/Aimport org.opensolaris.opengrok.history.Annotation;
1124N/Aimport org.opensolaris.opengrok.history.HistoryException;
1124N/Aimport org.opensolaris.opengrok.history.HistoryGuru;
0N/A
0N/A/**
1185N/A * Class for useful functions.
0N/A */
456N/Apublic final class Util {
1185N/A private Util() {
1185N/A // singleton
1185N/A }
1185N/A
92N/A /**
92N/A * Return a string which represents a <code>CharSequence</code> in HTML.
1190N/A *
1185N/A * @param q
1185N/A * a character sequence
92N/A * @return a string representing the character sequence in HTML
92N/A */
345N/A public static String htmlize(CharSequence q) {
0N/A StringBuilder sb = new StringBuilder(q.length() * 2);
345N/A htmlize(q, sb);
0N/A return sb.toString();
0N/A }
92N/A
92N/A /**
1185N/A * Append a character sequence to the given destination whereby
1185N/A * special characters for HTML are escaped accordingly.
1190N/A *
1185N/A * @param q a character sequence to esacpe
1185N/A * @param dest where to append the character sequence to
92N/A */
1185N/A public static void htmlize(CharSequence q, StringBuilder dest) {
1185N/A for (int i = 0; i < q.length(); i++ ) {
1185N/A htmlize(q.charAt(i), dest);
1185N/A }
1185N/A }
1185N/A
1185N/A /**
1185N/A * Append a character array to the given destination whereby
1185N/A * special characters for HTML are escaped accordingly.
1190N/A *
1185N/A * @param cs characters to esacpe
1185N/A * @param length max. number of characters to append, starting from index 0.
1185N/A * @param dest where to append the character sequence to
1185N/A */
1185N/A public static void htmlize(char[] cs, int length, StringBuilder dest) {
1185N/A if (cs.length < length) {
1185N/A length = cs.length;
1185N/A }
1185N/A for (int i = 0; i < length; i++ ) {
1185N/A htmlize(cs[i], dest);
0N/A }
0N/A }
92N/A
92N/A /**
1190N/A * Append a character to the given destination whereby special characters
1185N/A * special for HTML are escaped accordingly.
1190N/A *
1185N/A * @param c the character to append
1185N/A * @param dest where to append the character to
92N/A */
1185N/A private static final void htmlize(char c, StringBuilder dest) {
1185N/A switch (c) {
1185N/A case '&':
1185N/A dest.append("&amp;");
1185N/A break;
1185N/A case '>':
1185N/A dest.append("&gt;");
1185N/A break;
1185N/A case '<':
1185N/A dest.append("&lt;");
1185N/A break;
1185N/A case '\n':
1185N/A dest.append("<br/>");
1185N/A break;
1185N/A default:
1185N/A dest.append(c);
92N/A }
92N/A }
92N/A
1185N/A private static String versionP = htmlize(Info.getRevision());
1185N/A
92N/A /**
1185N/A * used by BUI - CSS needs this parameter for proper cache refresh (per
1185N/A * changeset) in client browser jel: but useless, since the page cached
1185N/A * anyway.
1190N/A *
990N/A * @return html escaped version (hg changeset)
990N/A */
990N/A public static String versionParameter() {
990N/A return versionP;
990N/A }
990N/A
604N/A /**
1185N/A * Convinience method for {@code breadcrumbPath(urlPrefix, path, '/')}.
1185N/A * @param urlPrefix prefix to add to each url
1185N/A * @param path path to crack
1185N/A * @return HTML markup fro the breadcrumb or the path itself.
1190N/A *
604N/A * @see #breadcrumbPath(String, String, char)
604N/A */
1185N/A public static String breadcrumbPath(String urlPrefix, String path) {
1185N/A return breadcrumbPath(urlPrefix, path, '/');
0N/A }
154N/A
583N/A private static final String anchorLinkStart = "<a href=\"";
583N/A private static final String anchorClassStart = "<a class=\"";
583N/A private static final String anchorEnd = "</a>";
583N/A private static final String closeQuotedTag = "\">";
604N/A
604N/A /**
1190N/A * Convinience method for
1185N/A * {@code breadcrumbPath(urlPrefix, path, sep, "", false)}.
1190N/A *
1185N/A * @param urlPrefix prefix to add to each url
1185N/A * @param path path to crack
1185N/A * @param sep separator to use to crack the given path
1190N/A *
1185N/A * @return HTML markup fro the breadcrumb or the path itself.
1185N/A * @see #breadcrumbPath(String, String, char, String, boolean, boolean)
604N/A */
1185N/A public static String breadcrumbPath(String urlPrefix, String path, char sep)
1185N/A {
1185N/A return breadcrumbPath(urlPrefix, path, sep, "", false);
1185N/A }
1185N/A
1185N/A /**
1190N/A * Convinience method for
1185N/A * {@code breadcrumbPath(urlPrefix, path, sep, "", false, path.endsWith(sep)}.
1190N/A *
1185N/A * @param urlPrefix prefix to add to each url
1185N/A * @param path path to crack
1185N/A * @param sep separator to use to crack the given path
1185N/A * @param urlPostfix suffix to add to each url
1185N/A * @param compact if {@code true} the given path gets transformed into
1190N/A * its canonical form (.i.e. all '.' and '..' and double separators
1190N/A * removed, but not always resolves to an absolute path) before processing
1185N/A * starts.
1185N/A * @return HTML markup fro the breadcrumb or the path itself.
1185N/A * @see #breadcrumbPath(String, String, char, String, boolean, boolean)
1185N/A * @see #getCanonicalPath(String, char)
1185N/A */
1185N/A public static String breadcrumbPath(String urlPrefix, String path,
1185N/A char sep, String urlPostfix, boolean compact)
1185N/A {
1185N/A if (path == null || path.length() == 0) {
1185N/A return path;
1185N/A }
1185N/A return breadcrumbPath(urlPrefix, path, sep, urlPostfix, compact,
1185N/A path.charAt(path.length() - 1) == sep);
604N/A }
604N/A
604N/A /**
604N/A * Create a breadcrumb path to allow navigation to each element of a path.
1185N/A * Consecutive separators (<var>sep</var>) in the given <var>path</var> are
1185N/A * always collapsed into a single separator automatically. If
1185N/A * <var>compact</var> is {@code true} path gets translated into a canonical
1185N/A * path similar to {@link File#getCanonicalPath()}, however the current
1185N/A * working directory is assumed to be "/" and no checks are done (e.g.
1185N/A * neither whether the path [component] exists nor which type it is).
1190N/A *
1185N/A * @param urlPrefix
1185N/A * what should be prepend to the constructed URL
1185N/A * @param path
1185N/A * the full path from which the breadcrumb path is built.
1185N/A * @param sep
1185N/A * the character that separates the path components in
1185N/A * <var>path</var>
1185N/A * @param urlPostfix
1185N/A * what should be append to the constructed URL
1185N/A * @param compact
1185N/A * if {@code true}, a canonical path gets constructed before
1185N/A * processing.
1185N/A * @param isDir
1185N/A * if {@code true} a "/" gets append to the last path component's
1185N/A * link and <var>sep</var> to its name
1190N/A * @return <var>path</var> if it resolves to an empty or "/" or
1185N/A * {@code null} path, the HTML markup for the breadcrumb path otherwise.
604N/A */
1185N/A public static String breadcrumbPath(String urlPrefix, String path,
1185N/A char sep, String urlPostfix, boolean compact, boolean isDir)
1185N/A {
1185N/A if (path == null || path.length() == 0) {
1185N/A return path;
1185N/A }
1185N/A String[] pnames = normalize(path.split(escapeForRegex(sep)), compact);
1185N/A if (pnames.length == 0) {
1185N/A return path;
1185N/A }
1185N/A if (urlPrefix == null) {
1185N/A urlPrefix = "";
1185N/A }
1185N/A if (urlPostfix == null) {
1185N/A urlPostfix = "";
154N/A }
1185N/A StringBuilder pwd = new StringBuilder(path.length() + pnames.length);
1185N/A StringBuilder markup =
1185N/A new StringBuilder( (pnames.length + 3 >> 1) * path.length()
1185N/A + pnames.length
1185N/A * (17 + urlPrefix.length() + urlPostfix.length()));
1185N/A int k = path.indexOf(pnames[0]);
1185N/A if (path.lastIndexOf(sep, k) != -1) {
1185N/A pwd.append('/');
1185N/A markup.append(sep);
604N/A }
1185N/A for (int i = 0; i < pnames.length; i++ ) {
1185N/A pwd.append(URIEncodePath(pnames[i]));
1185N/A if (isDir || i < pnames.length - 1) {
1185N/A pwd.append('/');
1185N/A }
1185N/A markup.append(anchorLinkStart).append(urlPrefix).append(pwd)
1185N/A .append(urlPostfix).append(closeQuotedTag).append(pnames[i])
1185N/A .append(anchorEnd);
1185N/A if (isDir || i < pnames.length - 1) {
1185N/A markup.append(sep);
1185N/A }
1185N/A }
1185N/A return markup.toString();
604N/A }
604N/A
604N/A /**
1190N/A * Normalize the given <var>path</var> to its canonical form. I.e. all
1190N/A * separators (<var>sep</var>) are replaced with a slash ('/'), all
1185N/A * double slashes are replaced by a single slash, all single dot path
1190N/A * components (".") of the formed path are removed and all double dot path
1190N/A * components (".." ) of the formed path are replaced with its parent or
1185N/A * '/' if there is no parent.
1185N/A * <p>
1185N/A * So the difference to {@link File#getCanonicalPath()} is, that this method
1185N/A * does not hit the disk (just string manipulation), resolves <var>path</var>
1190N/A * always against '/' and thus always returns an absolute path, which may
1190N/A * actually not exist, and which has a single trailing '/' if the given
1190N/A * <var>path</var> ends with the given <var>sep</var>.
1190N/A *
1190N/A * @param path path to mangle. If not absolute or {@code null}, the
1185N/A * current working directory is assumed to be '/'.
1185N/A * @param sep file separator to use to crack <var>path</var> into path
1185N/A * components
1185N/A * @return always a canonical path which starts with a '/'.
604N/A */
1185N/A public static String getCanonicalPath(String path, char sep) {
1185N/A if (path == null || path.length() == 0) {
1185N/A return "/";
1185N/A }
1185N/A String[] pnames = normalize(path.split(escapeForRegex(sep)), true);
1185N/A if (pnames.length == 0) {
1185N/A return "/";
604N/A }
1185N/A StringBuilder buf = new StringBuilder(path.length());
1185N/A buf.append('/');
1185N/A for (int i=0; i < pnames.length; i++) {
1185N/A buf.append(pnames[i]).append('/');
0N/A }
1185N/A if (path.charAt(path.length()-1) != sep) {
1190N/A // since is not a general purpose method. So we waive to handle
1185N/A // cases like:
1185N/A // || path.endsWith("/..") || path.endsWith("/.")
1185N/A buf.setLength(buf.length()-1);
1185N/A }
1185N/A return buf.toString();
604N/A }
604N/A
604N/A /**
1185N/A * Remove all empty and {@code null} string elements from the given
1185N/A * <var>names</var> and optionally all redundant information like "." and
1185N/A * "..".
1190N/A *
1185N/A * @param names
1185N/A * names to check
1185N/A * @param canonical
1185N/A * if {@code true}, remove redundant elements as well.
1185N/A * @return a possible empty array of names all with a length &gt; 0.
604N/A */
1185N/A private static String[] normalize(String[] names, boolean canonical) {
1185N/A LinkedList<String> res = new LinkedList<String>();
1185N/A if (names == null || names.length == 0) {
1185N/A return new String[0];
1185N/A }
1185N/A for (int i = 0; i < names.length; i++ ) {
1185N/A if (names[i] == null || names[i].length() == 0) {
1185N/A continue;
1185N/A }
1185N/A if (canonical) {
1185N/A if (names[i].equals("..")) {
1185N/A if (res.size() > 0) {
1185N/A res.removeLast();
605N/A }
1185N/A } else if (names[i].equals(".")) {
1185N/A continue;
1185N/A } else {
1185N/A res.add(names[i]);
605N/A }
605N/A } else {
1185N/A res.add(names[i]);
605N/A }
605N/A }
1185N/A return res.size() == names.length ? names : res.toArray(new String[res
1185N/A .size()]);
604N/A }
604N/A
604N/A /**
1185N/A * Generate a regex that matches the specified character. Escape it in case
1185N/A * it is a character that has a special meaning in a regex.
1190N/A *
1185N/A * @param c
1185N/A * the character that the regex should match
604N/A * @return a six-character string on the form <tt>&#92;u</tt><i>hhhh</i>
604N/A */
604N/A private static String escapeForRegex(char c) {
604N/A StringBuilder sb = new StringBuilder(6);
604N/A sb.append("\\u");
1185N/A String hex = Integer.toHexString(c);
1185N/A for (int i = 0; i < 4 - hex.length(); i++ ) {
604N/A sb.append('0');
0N/A }
604N/A sb.append(hex);
604N/A return sb.toString();
0N/A }
154N/A
1185N/A static NumberFormat FORMATTER = new DecimalFormat("#,###,###,###.#");
1185N/A
1185N/A /**
1185N/A * Convert the given size into a human readable string.
1185N/A * @param num size to convert.
1185N/A * @return a readable string
1185N/A */
1185N/A public static String readableSize(long num) {
1185N/A float l = num;
1185N/A NumberFormat formatter = (NumberFormat) FORMATTER.clone();
154N/A if (l < 1024) {
1185N/A return formatter.format(l) + ' '; // for none-dirs append 'B'? ...
154N/A } else if (l < 1048576) {
1185N/A return (formatter.format(l / 1024) + " KiB");
0N/A } else {
1185N/A return ("<b>" + formatter.format(l / 1048576) + " MiB</b>");
0N/A }
0N/A }
154N/A
878N/A /**
1185N/A * Converts different html special characters into their encodings used in
1185N/A * html. Currently used only for tooltips of annotation revision number view
1190N/A *
1185N/A * @param s
1185N/A * input text
878N/A * @return encoded text for use in <a title=""> tag
878N/A */
878N/A public static String encode(String s) {
972N/A StringBuffer sb = new StringBuffer();
1185N/A for (int i = 0; i < s.length(); i++ ) {
972N/A char c = s.charAt(i);
972N/A switch (c) {
972N/A case '"':
972N/A sb.append('\'');
972N/A break; // \\\"
972N/A case '&':
972N/A sb.append("&amp;");
972N/A break;
972N/A case '>':
972N/A sb.append("&gt;");
972N/A break;
972N/A case '<':
972N/A sb.append("&lt;");
972N/A break;
972N/A case ' ':
972N/A sb.append("&nbsp;");
972N/A break;
972N/A case '\t':
972N/A sb.append("&nbsp;&nbsp;&nbsp;&nbsp;");
972N/A break;
972N/A case '\n':
1185N/A sb.append("&lt;br/&gt;");
972N/A break;
972N/A case '\r':
972N/A break;
972N/A default:
972N/A sb.append(c);
972N/A break;
972N/A }
972N/A }
972N/A return sb.toString();
972N/A }
878N/A
1185N/A /**
1190N/A * Write out line information wrt. to the given annotation in the format:
1185N/A * {@code Linenumber Blame Author} incl. appropriate links.
1190N/A *
1185N/A * @param num linenumber to print
1185N/A * @param out print destination
1190N/A * @param annotation annotation to use. If {@code null} only the
1185N/A * linenumber gets printed.
1185N/A * @param userPageLink see {@link RuntimeEnvironment#getUserPage()}
1185N/A * @param userPageSuffix see {@link RuntimeEnvironment#getUserPageSuffix()}
1185N/A * @throws IOException depends on the destination (<var>out</var>).
1185N/A */
1185N/A public static void readableLine(int num, Writer out, Annotation annotation,
1185N/A String userPageLink, String userPageSuffix)
1185N/A throws IOException
1185N/A {
1185N/A // this method should go to JFlexXref
0N/A String snum = String.valueOf(num);
54N/A if (num > 1) {
54N/A out.write("\n");
54N/A }
583N/A out.write(anchorClassStart);
1185N/A out.write( (num % 10 == 0 ? "hl" : "l"));
0N/A out.write("\" name=\"");
0N/A out.write(snum);
847N/A out.write("\" href=\"#");
847N/A out.write(snum);
583N/A out.write(closeQuotedTag);
0N/A out.write(snum);
583N/A out.write(anchorEnd);
89N/A if (annotation != null) {
89N/A String r = annotation.getRevision(num);
168N/A boolean enabled = annotation.isEnabled(num);
1185N/A out.write("<span class=\"blame\">");
168N/A if (enabled) {
1185N/A out.write(anchorClassStart);
1185N/A out.write("r\" href=\"" );
168N/A out.write(URIEncode(annotation.getFilename()));
850N/A out.write("?a=true&amp;r=");
168N/A out.write(URIEncode(r));
1185N/A String msg = annotation.getDesc(r);
1185N/A if (msg != null) {
1185N/A out.write("\" title=\"");
1185N/A out.write(msg);
878N/A }
583N/A out.write(closeQuotedTag);
168N/A }
1185N/A StringBuilder buf = new StringBuilder();
1185N/A htmlize(r, buf);
1185N/A out.write(buf.toString());
1185N/A buf.setLength(0);
168N/A if (enabled) {
583N/A out.write(anchorEnd);
168N/A }
89N/A String a = annotation.getAuthor(num);
1185N/A if (userPageLink != null) {
1185N/A out.write(anchorClassStart);
1185N/A out.write("a\" href=\"");
1185N/A out.write(userPageLink);
259N/A out.write(URIEncode(a));
1185N/A if (userPageSuffix != null) {
1185N/A out.write(userPageSuffix);
1100N/A }
583N/A out.write(closeQuotedTag);
1185N/A htmlize(a, buf);
1185N/A out.write(buf.toString());
1185N/A buf.setLength(0);
583N/A out.write(anchorEnd);
259N/A } else {
1185N/A out.write("<span class=\"a\">");
1185N/A htmlize(a, buf);
1185N/A out.write(buf.toString());
1185N/A out.write("</span>");
1185N/A buf.setLength(0);
259N/A }
1185N/A out.write("</span>");
89N/A }
0N/A }
154N/A
0N/A /**
1185N/A * Generate a string from the given path and date in a way that allows
1190N/A * stable lexicographic sorting (i.e. gives always the same results) as a
1190N/A * walk of the file hierarchy. Thus null character (\u0000) is used both
1185N/A * to separate directory components and to separate the path from the date.
1185N/A * @param path path to mangle.
1185N/A * @param date date string to use.
1185N/A * @return the mangled path.
0N/A */
1185N/A public static String path2uid(String path, String date) {
153N/A return path.replace('/', '\u0000') + "\u0000" + date;
0N/A }
154N/A
1185N/A /**
1190N/A * The reverse operation for {@link #path2uid(String, String)} - re-creates
1185N/A * the unmangled path from the given uid.
1185N/A * @param uid uid to unmangle.
1185N/A * @return the original path.
1185N/A */
0N/A public static String uid2url(String uid) {
1185N/A String url = uid.replace('\u0000', '/');
0N/A return url.substring(0, url.lastIndexOf('/')); // remove date from end
0N/A }
154N/A
849N/A /**
849N/A * wrapper arround UTF-8 URL encoding of a string
1190N/A *
1185N/A * @param q query to be encoded. If {@code null}, an empty string will
1185N/A * be used instead.
849N/A * @return null if fail, otherwise the encoded string
1185N/A * @see URLEncoder#encode(String, String)
849N/A */
0N/A public static String URIEncode(String q) {
1185N/A try {
1185N/A return q == null ? "" : URLEncoder.encode(q, "UTF-8");
159N/A } catch (UnsupportedEncodingException e) {
830N/A // Should not happen. UTF-8 must be supported by JVMs.
0N/A }
1185N/A return null;
0N/A }
154N/A
1185N/A /**
1185N/A * Append '&amp;name=value" to the given buffer. If the given <var>value</var>
1185N/A * is {@code null}, this method does nothing.
1190N/A *
1185N/A * @param buf where to append the query string
1185N/A * @param key the name of the parameter to add. Append as is!
1185N/A * @param value the value for the given parameter. Gets automatically UTF-8
1185N/A * URL encoded.
1185N/A * @throws NullPointerException if the given buffer is {@code null}.
1185N/A * @see #URIEncode(String)
1185N/A */
1190N/A public static final void appendQuery(StringBuilder buf, String key,
1190N/A String value)
1185N/A {
1185N/A if (value != null) {
1185N/A buf.append("&amp;").append(key).append('=').append(URIEncode(value));
1185N/A }
1185N/A }
1190N/A
1185N/A /**
1185N/A * URI encode the given path.
1185N/A * @param path path to encode.
1185N/A * @return the encoded path.
1185N/A * @see URI#getRawPath()
1185N/A * @throws NullPointerException if a parameter is {@code null}
1185N/A */
158N/A public static String URIEncodePath(String path) {
292N/A try {
1185N/A URI uri = new URI(null, null, path, null);
1185N/A return uri.getRawPath();
292N/A } catch (URISyntaxException ex) {
1185N/A OpenGrokLogger.getLogger().log(Level.WARNING,
1185N/A "Could not encode path " + path, ex);
158N/A }
1185N/A return "";
158N/A }
158N/A
1185N/A /**
1185N/A * Replace all quote characters (ASCI 0x22) with the corresponding html
1190N/A * entity (&amp;quot;).
1185N/A * @param q string to escape.
1190N/A * @return an empty string if a parameter is {@code null}, the mangled
1185N/A * string otherwise.
1185N/A */
0N/A public static String formQuoteEscape(String q) {
1185N/A if (q == null || q.isEmpty()) {
58N/A return "";
58N/A }
0N/A StringBuilder sb = new StringBuilder();
0N/A char c;
1185N/A for (int i = 0; i < q.length(); i++ ) {
0N/A c = q.charAt(i);
154N/A if (c == '"') {
0N/A sb.append("&quot;");
0N/A } else {
0N/A sb.append(c);
0N/A }
0N/A }
0N/A return sb.toString();
0N/A }
1025N/A
1185N/A private static final String SPAN_D = "<span class=\"d\">";
1185N/A private static final String SPAN_A = "<span class=\"a\">";
1185N/A private static final String SPAN_E = "</span>";
1185N/A private static final int SPAN_LEN = SPAN_D.length() + SPAN_E.length();
1190N/A
1025N/A /**
1185N/A * Tag changes in the given <var>line1</var> and <var>line2</var>
1185N/A * for highlighting. Removed parts are tagged with CSS class {@code d},
1185N/A * new parts are tagged with CSS class {@code a} using a {@code span}
1185N/A * element.
1190N/A *
1185N/A * @param line1 line of the original file
1185N/A * @param line2 line of the changed/new file
1185N/A * @return the tagged lines (field[0] ~= line1, field[1] ~= line2).
1190N/A * @throws NullPointerException if one of the given parameters is {@code null}.
1025N/A */
1185N/A public static String[] diffline(StringBuilder line1, StringBuilder line2) {
1185N/A int m = line1.length();
1185N/A int n = line2.length();
1185N/A if (n == 0 || m == 0) {
1185N/A return new String[] { line1.toString(), line2.toString() };
1185N/A }
1025N/A
1185N/A int s = 0;
1185N/A char[] csl1 = new char[m + SPAN_LEN];
1185N/A line1.getChars(0, m--, csl1, 0);
1185N/A char[] csl2 = new char[n + SPAN_LEN];
1185N/A line2.getChars(0, n--, csl2, 0);
1185N/A while (s <= m && s <= n && csl1[s] == csl2[s]) {
1185N/A s++ ;
1185N/A }
1185N/A while (s <= m && s <= n && csl1[m] == csl2[n]) {
1185N/A m-- ;
1185N/A n-- ;
1185N/A }
1025N/A
1185N/A String[] ret = new String[2];
1185N/A // deleted
1185N/A if (s <= m) {
1185N/A m++;
1185N/A System.arraycopy(csl1, m, csl1, m + SPAN_LEN, line1.length() - m);
1185N/A System.arraycopy(csl1, s, csl1, s + SPAN_D.length(), m - s);
1185N/A SPAN_E.getChars(0, SPAN_E.length(), csl1, m + SPAN_D.length());
1185N/A SPAN_D.getChars(0, SPAN_D.length(), csl1, s);
1185N/A ret[0] = new String(csl1);
1185N/A } else {
1185N/A ret[0] = line1.toString();
1185N/A }
1185N/A // added
1185N/A if (s <= n) {
1185N/A n++;
1185N/A System.arraycopy(csl2, n, csl2, n + SPAN_LEN, line2.length() - n);
1185N/A System.arraycopy(csl2, s, csl2, s + SPAN_A.length(), n - s);
1185N/A SPAN_E.getChars(0, SPAN_E.length(), csl2, n + SPAN_A.length());
1185N/A SPAN_A.getChars(0, SPAN_A.length(), csl2, s);
1185N/A ret[1] = new String(csl2);
1185N/A } else {
1185N/A ret[1] = line2.toString();
1185N/A }
1185N/A return ret;
1025N/A }
1124N/A
1124N/A /**
1124N/A * Dump the configuration as an HTML table.
1190N/A *
1185N/A * @param out
1185N/A * destination for the HTML output
1185N/A * @throws IOException
1185N/A * if an error happens while writing to {@code out}
1185N/A * @throws HistoryException
1185N/A * if the history guru cannot be accesses
1124N/A */
1185N/A @SuppressWarnings("boxing")
1185N/A public static void dumpConfiguration(Appendable out) throws IOException,
1185N/A HistoryException
1185N/A {
1124N/A out.append("<table border=\"1\" width=\"100%\">");
1124N/A out.append("<tr><th>Variable</th><th>Value</th></tr>");
1124N/A RuntimeEnvironment env = RuntimeEnvironment.getInstance();
1124N/A printTableRow(out, "Source root", env.getSourceRootPath());
1124N/A printTableRow(out, "Data root", env.getDataRootPath());
1124N/A printTableRow(out, "CTags", env.getCtags());
1124N/A printTableRow(out, "Bug page", env.getBugPage());
1124N/A printTableRow(out, "Bug pattern", env.getBugPattern());
1124N/A printTableRow(out, "User page", env.getUserPage());
1124N/A printTableRow(out, "Review page", env.getReviewPage());
1124N/A printTableRow(out, "Review pattern", env.getReviewPattern());
1124N/A printTableRow(out, "Using projects", env.hasProjects());
1124N/A out.append("<tr><td>Ignored files</td><td>");
1124N/A printUnorderedList(out, env.getIgnoredNames().getItems());
1124N/A out.append("</td></tr>");
1124N/A printTableRow(out, "Index word limit", env.getIndexWordLimit());
1124N/A printTableRow(out, "Allow leading wildcard in search",
1185N/A env.isAllowLeadingWildcard());
1185N/A printTableRow(out, "History cache", HistoryGuru.getInstance()
1185N/A .getCacheInfo());
1124N/A out.append("</table>");
1124N/A }
1124N/A
1124N/A /**
1185N/A * Just read the given source and dump as is to the given destionation.
1190N/A * Does nothing, if one or more of the parameters is {@code null}.
1185N/A * @param out write destination
1185N/A * @param in source to read
1185N/A * @throws IOException as defined by the given reader/writer
1185N/A * @throws NullPointerException if a parameter is {@code null}.
1185N/A */
1185N/A public static final void dump(Writer out, Reader in) throws IOException {
1185N/A if (in == null || out == null) {
1185N/A return;
1185N/A }
1185N/A char[] buf = new char[8192];
1185N/A int len = 0;
1185N/A while ((len = in.read(buf)) >= 0) {
1185N/A out.write(buf, 0, len);
1185N/A }
1185N/A }
1190N/A
1185N/A /**
1190N/A * Silently dump a file to the given destionation. All {@link IOException}s
1185N/A * gets caught and logged, but not re-thrown.
1190N/A *
1185N/A * @param out dump destination
1185N/A * @param dir directory, which should contains the file.
1185N/A * @param filename the basename of the file to dump.
1185N/A * @param compressed if {@code true} the denoted file is assumed to be
1190N/A * gzipped.
1185N/A * @return {@code true} on success (everything read and written).
1185N/A * @throws NullPointerException if a parameter is {@code null}.
1185N/A */
1190N/A public static boolean dump(Writer out, File dir, String filename,
1190N/A boolean compressed)
1185N/A {
1185N/A return dump(out, new File(dir, filename), compressed);
1185N/A }
1185N/A
1185N/A /**
1190N/A * Silently dump a file to the given destionation. All {@link IOException}s
1185N/A * gets caught and logged, but not re-thrown.
1190N/A *
1185N/A * @param out dump destination
1185N/A * @param file file to dump.
1185N/A * @param compressed if {@code true} the denoted file is assumed to be
1190N/A * gzipped.
1185N/A * @return {@code true} on success (everything read and written).
1185N/A * @throws NullPointerException if a parameter is {@code null}.
1185N/A */
1185N/A public static boolean dump(Writer out, File file, boolean compressed) {
1185N/A if (!file.exists()) {
1185N/A return false;
1185N/A }
1185N/A FileInputStream fis = null;
1185N/A GZIPInputStream gis = null;
1185N/A Reader in = null;
1185N/A try {
1185N/A if (compressed) {
1185N/A fis = new FileInputStream(file);
1185N/A gis = new GZIPInputStream(fis);
1185N/A in = new InputStreamReader(gis);
1185N/A } else {
1185N/A in = new FileReader(file);
1185N/A }
1185N/A dump(out, in);
1185N/A return true;
1185N/A } catch(IOException e) {
1185N/A OpenGrokLogger.getLogger().log(Level.WARNING,
1185N/A "An error occured while piping file " + file + ": ", e);
1185N/A } finally {
1185N/A if (in != null) {
1190N/A try { in.close(); gis = null; fis = null; }
1185N/A catch (Exception e) { /**/ }
1185N/A }
1185N/A if (gis != null) {
1185N/A try { gis.close(); fis = null; }
1185N/A catch (Exception e) { /**/ }
1185N/A }
1185N/A if (fis != null) {
1185N/A try { fis.close();}
1185N/A catch (Exception e) { /**/ }
1185N/A }
1185N/A }
1185N/A return false;
1185N/A }
1185N/A
1185N/A /**
1124N/A * Print a row in an HTML table.
1190N/A *
1185N/A * @param out
1185N/A * destination for the HTML output
1185N/A * @param cells
1185N/A * the values to print in the cells of the row
1185N/A * @throws IOException
1185N/A * if an error happens while writing to {@code out}
1124N/A */
1124N/A private static void printTableRow(Appendable out, Object... cells)
1185N/A throws IOException
1185N/A {
1124N/A out.append("<tr>");
1185N/A StringBuilder buf = new StringBuilder(256);
1124N/A for (Object cell : cells) {
1124N/A out.append("<td>");
1124N/A String str = (cell == null) ? "null" : cell.toString();
1185N/A htmlize(str, buf);
1185N/A out.append(str);
1185N/A buf.setLength(0);
1124N/A out.append("</td>");
1124N/A }
1124N/A out.append("</tr>");
1124N/A }
1124N/A
1124N/A /**
1124N/A * Print an unordered list (HTML).
1190N/A *
1185N/A * @param out
1185N/A * destination for the HTML output
1185N/A * @param items
1185N/A * the list items
1185N/A * @throws IOException
1185N/A * if an error happens while writing to {@code out}
1124N/A */
1185N/A private static void printUnorderedList(Appendable out,
1185N/A Collection<String> items) throws IOException
1185N/A {
1124N/A out.append("<ul>");
1185N/A StringBuilder buf = new StringBuilder(256);
1124N/A for (String item : items) {
1124N/A out.append("<li>");
1185N/A htmlize(item, buf);
1185N/A out.append(buf);
1185N/A buf.setLength(0);
1124N/A out.append("</li>");
1124N/A }
1124N/A out.append("</ul>");
1124N/A }
1145N/A
1145N/A /**
1145N/A * Create a string literal for use in JavaScript functions.
1190N/A *
1185N/A * @param str
1185N/A * the string to be represented by the literal
1145N/A * @return a JavaScript string literal
1145N/A */
1145N/A public static String jsStringLiteral(String str) {
1145N/A StringBuilder sb = new StringBuilder();
1145N/A sb.append('"');
1185N/A for (int i = 0; i < str.length(); i++ ) {
1145N/A char c = str.charAt(i);
1145N/A switch (c) {
1145N/A case '"':
1145N/A sb.append("\\\"");
1145N/A break;
1145N/A case '\\':
1145N/A sb.append("\\\\");
1145N/A break;
1145N/A case '\n':
1145N/A sb.append("\\n");
1145N/A break;
1145N/A case '\r':
1145N/A sb.append("\\r");
1145N/A break;
1145N/A default:
1145N/A sb.append(c);
1145N/A }
1145N/A }
1145N/A sb.append('"');
1145N/A return sb.toString();
1145N/A }
0N/A}