All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.detector.api.TextFormat Maven / Gradle / Ivy

There is a newer version: 25.3.0
Show newest version
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.lint.detector.api;

import com.android.annotations.NonNull;
import com.android.utils.SdkUtils;
import com.android.utils.XmlUtils;

/**
 * Lint error message, issue explanations and location descriptions
 * are described in a {@link #RAW} format which looks similar to text
 * but which can contain bold, symbols and links. These issues can
 * also be converted to plain text and to HTML markup, using the
 * {@link #convertTo(String, TextFormat)} method.
 *
 * @see Issue#getDescription(TextFormat)
 * @see Issue#getExplanation(TextFormat)
 * @see Issue#getBriefDescription(TextFormat)
 */
public enum TextFormat {
    /**
     * Raw output format which is similar to text but allows some markup:
     * 
    *
  • HTTP urls (http://...) *
  • Sentences immediately surrounded by * will be shown as bold. *
  • Sentences immediately surrounded by ` will be shown using monospace * fonts *
* Furthermore, newlines are converted to br's when converting newlines. * Note: It does not insert {@code } tags around the fragment for HTML output. *

* TODO: Consider switching to the restructured text format - * http://docutils.sourceforge.net/docs/user/rst/quickstart.html */ RAW, /** * Plain text output */ TEXT, /** * HTML formatted output (note: does not include surrounding {@code } tags) */ HTML; /** * Converts the given text to HTML * * @param text the text to format * @return the corresponding text formatted as HTML */ @NonNull public String toHtml(@NonNull String text) { return convertTo(text, HTML); } /** * Converts the given text to plain text * * @param text the tetx to format * @return the corresponding text formatted as HTML */ @NonNull public String toText(@NonNull String text) { return convertTo(text, TEXT); } /** * Converts the given message to the given format. Note that some * conversions are lossy; e.g. once converting away from the raw format * (which contains all the markup) you can't convert back to it. * Note that you can convert to the format it's already in; that just * returns the same string. * * @param message the message to convert * @param to the format to convert to * @return a converted message */ public String convertTo(@NonNull String message, @NonNull TextFormat to) { if (this == to) { return message; } switch (this) { case RAW: { switch (to) { case RAW: return message; case TEXT: case HTML: return to.fromRaw(message); } } case TEXT: { switch (to) { case TEXT: case RAW: return message; case HTML: return XmlUtils.toXmlTextValue(message); } } case HTML: { switch (to) { case HTML: return message; case RAW: case TEXT: { return to.fromHtml(message); } } } } return message; } /** Converts to this output format from the given HTML-format text */ @NonNull private String fromHtml(@NonNull String html) { assert this == RAW || this == TEXT : this; // Drop all tags; replace all entities, insert newlines // (this won't do wrapping) StringBuilder sb = new StringBuilder(html.length()); for (int i = 0, n = html.length(); i < n; i++) { char c = html.charAt(i); if (c == '<') { // Scan forward to the end if (html.startsWith("
", i) || html.startsWith("
", i) || html.startsWith("
", i) || html.startsWith("
", i)) { sb.append('\n'); } else if (html.startsWith("", i)); } i = html.indexOf('>', i); } else if (c == '&') { int end = html.indexOf(';', i); if (end > i) { String entity = html.substring(i, end + 1); sb.append(XmlUtils.fromXmlAttributeValue(entity)); i = end; } else { sb.append(c); } } else if (c == '\n') { sb.append(' '); } else { sb.append(c); } } // Collapse repeated spaces String s = sb.toString(); sb.setLength(0); boolean wasSpace = false; for (int i = 0, n = s.length(); i < n; i++) { char c = s.charAt(i); if (c == '\t') { // we keep newlines; came from
's c = ' '; } boolean isSpace = c == ' '; if (!isSpace || !wasSpace) { wasSpace = isSpace; sb.append(c); } } s = sb.toString(); // Line-wrap s = SdkUtils.wrap(s, 60, null); return s; } private static final String HTTP_PREFIX = "http://"; //$NON-NLS-1$ /** Converts to this output format from the given raw-format text */ @NonNull private String fromRaw(@NonNull String text) { assert this == HTML || this == TEXT : this; StringBuilder sb = new StringBuilder(3 * text.length() / 2); boolean html = this == HTML; char prev = 0; int flushIndex = 0; int n = text.length(); for (int i = 0; i < n; i++) { char c = text.charAt(i); if ((c == '*' || c == '`' && i < n - 1)) { // Scout ahead for range end if (!Character.isLetterOrDigit(prev) && !Character.isWhitespace(text.charAt(i + 1))) { // Found * or ` immediately before a letter, and not in the middle of a word // Find end int end = text.indexOf(c, i + 1); if (end != -1 && (end == n - 1 || !Character.isLetter(text.charAt(end + 1)))) { if (i > flushIndex) { appendEscapedText(sb, text, html, flushIndex, i); } if (html) { String tag = c == '*' ? "b" : "code"; //$NON-NLS-1$ //$NON-NLS-2$ sb.append('<').append(tag).append('>'); appendEscapedText(sb, text, html, i + 1, end); sb.append('<').append('/').append(tag).append('>'); } else { appendEscapedText(sb, text, html, i + 1, end); } flushIndex = end + 1; i = flushIndex - 1; // -1: account for the i++ in the loop } } } else if (html && c == 'h' && i < n - 1 && text.charAt(i + 1) == 't' && text.startsWith(HTTP_PREFIX, i) && !Character.isLetterOrDigit(prev)) { // Find url end int end = i + HTTP_PREFIX.length(); while (end < n) { char d = text.charAt(end); if (Character.isWhitespace(d)) { break; } end++; } char last = text.charAt(end - 1); if (last == '.' || last == ')' || last == '!') { end--; } if (end > i + HTTP_PREFIX.length()) { if (i > flushIndex) { appendEscapedText(sb, text, html, flushIndex, i); } String url = text.substring(i, end); sb.append("'); sb.append(url); sb.append(""); //$NON-NLS-1$ flushIndex = end; i = flushIndex - 1; // -1: account for the i++ in the loop } } prev = c; } if (flushIndex < n) { appendEscapedText(sb, text, html, flushIndex, n); } return sb.toString(); } private static void appendEscapedText(@NonNull StringBuilder sb, @NonNull String text, boolean html, int start, int end) { if (html) { for (int i = start; i < end; i++) { char c = text.charAt(i); if (c == '<') { sb.append("<"); //$NON-NLS-1$ } else if (c == '&') { sb.append("&"); //$NON-NLS-1$ } else if (c == '\n') { sb.append("
\n"); } else { if (c > 255) { sb.append("&#"); //$NON-NLS-1$ sb.append(Integer.toString(c)); sb.append(';'); } else if (c == '\u00a0') { sb.append(" "); //$NON-NLS-1$ } else { sb.append(c); } } } } else { for (int i = start; i < end; i++) { char c = text.charAt(i); sb.append(c); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy