Files
jenkins/core/src/main/java/hudson/MarkupText.java
T
kohsuke c6acb5b8d5 [FIXED HUDSON-2137]
Merged revisions 27137,27564,27566-27567,27578,27586,27599,27603,27606-27609,27611,27616,27626-27632,27640,27654-27656,27687,27701,27801,27816,27819,27843-27845,28087,28158-28161 via svnmerge from 
https://www.dev.java.net/svn/hudson/branches/HUDSON-2137

........
  r27137 | kohsuke | 2010-02-08 08:46:16 -0800 (Mon, 08 Feb 2010) | 1 line
  
  experimenting with abstractions
........
  r27564 | kohsuke | 2010-02-16 07:58:26 -0800 (Tue, 16 Feb 2010) | 1 line
  
  bundling makes it easier for caller to handle ConsoleAnnotator
........
  r27566 | kohsuke | 2010-02-16 08:49:48 -0800 (Tue, 16 Feb 2010) | 1 line
  
  this should work with Runs, not AbstractBuilds
........
  r27567 | kohsuke | 2010-02-16 08:54:06 -0800 (Tue, 16 Feb 2010) | 1 line
  
  simplified
........
  r27578 | kohsuke | 2010-02-16 12:26:11 -0800 (Tue, 16 Feb 2010) | 1 line
  
  still a work in progress, but committing once to pull in trunk changes
........
  r27586 | kohsuke | 2010-02-16 14:35:37 -0800 (Tue, 16 Feb 2010) | 1 line
  
  making more progress
........
  r27599 | kohsuke | 2010-02-16 19:22:01 -0800 (Tue, 16 Feb 2010) | 2 lines
  
  hooking up the side that writes annotations.
  still a work in progress.
........
  r27603 | kohsuke | 2010-02-16 23:43:35 -0800 (Tue, 16 Feb 2010) | 3 lines
  
  what appears to me as the first cut of hooking up the reader side of the annotations.
  
  I haven't even run it yet --- this just passed the compilation, and I'm calling it a day.
........
  r27606 | kohsuke | 2010-02-17 09:15:16 -0800 (Wed, 17 Feb 2010) | 1 line
  
  demo instance hook up
........
  r27607 | kohsuke | 2010-02-17 11:27:02 -0800 (Wed, 17 Feb 2010) | 3 lines
  
  one more utility method.
  
  I never understood what the point of letting the skip method skip less.
........
  r27608 | kohsuke | 2010-02-17 11:56:23 -0800 (Wed, 17 Feb 2010) | 3 lines
  
  - moved LargeText subtype to its own class
  - skipping log output was done incorrectly --- it had to skip N bytes where it was skipping N chars.
  - hooked up console annotations for completed builds.
........
  r27609 | kohsuke | 2010-02-17 12:01:46 -0800 (Wed, 17 Feb 2010) | 1 line
  
  copyright header
........
  r27611 | kohsuke | 2010-02-17 13:18:10 -0800 (Wed, 17 Feb 2010) | 1 line
  
  introduced a mechanism to register extension during an unit test.
........
  r27616 | kohsuke | 2010-02-17 13:38:12 -0800 (Wed, 17 Feb 2010) | 1 line
  
  test case for console annotations
........
  r27626 | kohsuke | 2010-02-17 15:23:06 -0800 (Wed, 17 Feb 2010) | 1 line
  
  removing test ConsoleAnnotator as I write tests as tests
........
  r27627 | kohsuke | 2010-02-17 16:06:13 -0800 (Wed, 17 Feb 2010) | 1 line
  
  added a multi-phase cooperative locking mechanism useful during the debugging
........
  r27628 | kohsuke | 2010-02-17 16:06:41 -0800 (Wed, 17 Feb 2010) | 1 line
  
  testing progressive log annotation
........
  r27629 | kohsuke | 2010-02-17 16:20:32 -0800 (Wed, 17 Feb 2010) | 1 line
  
  adding a test for ConsoleAnnotation that's explicitly placed.
........
  r27630 | kohsuke | 2010-02-17 16:23:02 -0800 (Wed, 17 Feb 2010) | 1 line
  
  removing test ConsoleAnnotation as I write tests as tests
........
  r27631 | kohsuke | 2010-02-17 16:28:50 -0800 (Wed, 17 Feb 2010) | 1 line
  
  tweaking the abstraction a bit.
........
  r27632 | kohsuke | 2010-02-17 16:44:21 -0800 (Wed, 17 Feb 2010) | 1 line
  
  parameterized the context type so that this can be used in places other than the build console output, such as polling logs, slave logs, and so on.
........
  r27640 | kohsuke | 2010-02-17 17:27:52 -0800 (Wed, 17 Feb 2010) | 1 line
  
  bug fix
........
  r27654 | kohsuke | 2010-02-17 22:53:04 -0800 (Wed, 17 Feb 2010) | 1 line
  
  doc improvement
........
  r27655 | kohsuke | 2010-02-17 22:53:59 -0800 (Wed, 17 Feb 2010) | 1 line
  
  doc improvement
........
  r27656 | kohsuke | 2010-02-17 23:10:42 -0800 (Wed, 17 Feb 2010) | 1 line
  
  test case for adjacent tags
........
  r27687 | kohsuke | 2010-02-19 14:39:21 -0800 (Fri, 19 Feb 2010) | 1 line
  
  introduced a facotry and descriptor. This allows them to define configuration pages, allow users to disable/enable them, plus define custom behaviors.
........
  r27701 | kohsuke | 2010-02-19 18:23:52 -0800 (Fri, 19 Feb 2010) | 1 line
  
  defining a mechanism for ConsoleAnnotatorFactory and ConsoleAnnotationDescriptor to define script.js and have them imported into the console output page
........
  r27801 | kohsuke | 2010-02-22 11:59:16 -0800 (Mon, 22 Feb 2010) | 1 line
  
  now embedding annotations inside the console output log file.
........
  r27816 | kohsuke | 2010-02-22 15:45:04 -0800 (Mon, 22 Feb 2010) | 1 line
  
  introduced a convenience method for unit tests
........
  r27819 | kohsuke | 2010-02-22 17:37:05 -0800 (Mon, 22 Feb 2010) | 5 lines
  
  As an example of using embedded console annotations, implementing Ant target invocation outlines.
  
  It might be better to make the concept of outline more generally reusable, so that different kinds of outlines can be placed into the same container element for a single hierarchical representation.
  
  Still a work in progress.
........
  r27843 | kohsuke | 2010-02-23 16:12:13 -0800 (Tue, 23 Feb 2010) | 1 line
  
  for now, disable this for production usage, until I work out a better UI.
........
  r27844 | kohsuke | 2010-02-23 19:08:00 -0800 (Tue, 23 Feb 2010) | 1 line
  
  started working on annotating exception.
........
  r27845 | kohsuke | 2010-02-23 19:08:33 -0800 (Tue, 23 Feb 2010) | 1 line
  
  more TODOs for myself
........
  r28087 | kohsuke | 2010-03-01 10:03:55 -0800 (Mon, 01 Mar 2010) | 1 line
  
  formatting changes
........
  r28158 | kohsuke | 2010-03-02 17:00:00 -0800 (Tue, 02 Mar 2010) | 1 line
  
  ConsoleAnnotation and ConsoleAnnotator are too confusing.
........
  r28159 | kohsuke | 2010-03-02 17:02:02 -0800 (Tue, 02 Mar 2010) | 1 line
  
  doc improvement
........
  r28160 | kohsuke | 2010-03-02 17:05:14 -0800 (Tue, 02 Mar 2010) | 1 line
  
  more doc improvements.
........
  r28161 | kohsuke | 2010-03-02 17:11:59 -0800 (Tue, 02 Mar 2010) | 1 line
  
  more doc improvement
........


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@28173 71c3de6d-444a-0410-be80-ed276b4c234a
2010-03-03 15:37:55 +00:00

271 lines
8.5 KiB
Java

/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Mutable representation of string with HTML mark up.
*
* <p>
* This class is used to put mark up on plain text.
* See <a href="https://hudson.dev.java.net/source/browse/hudson/hudson/main/core/src/test/java/hudson/MarkupTextTest.java?view=markup">
* the test code</a> for a typical usage and its result.
*
* @author Kohsuke Kawaguchi
* @since 1.70
*/
public class MarkupText extends AbstractMarkupText {
private final String text;
/**
* Added mark up tags.
*/
private final List<Tag> tags = new ArrayList<Tag>();
/**
* Represents one mark up inserted into text.
*/
private static final class Tag implements Comparable<Tag> {
/**
* Char position of this tag in {@link MarkupText#text}.
* This tag is placed in front of the character of this index.
*/
private final int pos;
private final String markup;
public Tag(int pos, String markup) {
this.pos = pos;
this.markup = markup;
}
public int compareTo(Tag that) {
return this.pos-that.pos;
}
}
/**
* Represents a substring of a {@link MarkupText}.
*/
public final class SubText extends AbstractMarkupText {
private final int start,end;
private final int[] groups;
public SubText(Matcher m, int textOffset) {
start = m.start() + textOffset;
end = m.end() + textOffset;
int cnt = m.groupCount();
groups = new int[cnt*2];
for( int i=0; i<cnt; i++ ) {
groups[i*2 ] = m.start(i+1) + textOffset;
groups[i*2+1] = m.end(i+1) + textOffset;
}
}
@Override
public String getText() {
return text.substring(start,end);
}
@Override
public void addMarkup(int startPos, int endPos, String startTag, String endTag) {
MarkupText.this.addMarkup(startPos+start, endPos+start, startTag, endTag);
}
/**
* Surrounds this subtext with the specified start tag and the end tag.
*
* <p>
* Start/end tag text can contain special tokens "$0", "$1", ...
* and they will be replaced by their {@link #group(int) group match}.
* "\$" can be used to escape characters.
*/
public void surroundWith(String startTag, String endTag) {
addMarkup(0,length(),replace(startTag),replace(endTag));
}
/**
* Works like {@link #surroundWith(String, String)} except
* that the token replacement is not performed on parameters.
*/
public void surroundWithLiteral(String startTag, String endTag) {
addMarkup(0,length(),startTag,endTag);
}
/**
* Gets the start index of the captured group within {@link MarkupText#getText()}.
*
* @param groupIndex
* 0 means the start of the whole subtext. 1, 2, ... are
* groups captured by '(...)' in the regexp.
*/
public int start(int groupIndex) {
if(groupIndex==0) return start;
return groups[groupIndex*2-2];
}
/**
* Gets the start index of this subtext within {@link MarkupText#getText()}.
*/
public int start() {
return start;
}
/**
* Gets the end index of the captured group within {@link MarkupText#getText()}.
*/
public int end(int groupIndex) {
if(groupIndex==0) return end;
return groups[groupIndex*2-1];
}
/**
* Gets the end index of this subtext within {@link MarkupText#getText()}.
*/
public int end() {
return end;
}
/**
* Gets the text that represents the captured group.
*/
public String group(int groupIndex) {
if(start(groupIndex)==-1)
return null;
return text.substring(start(groupIndex),end(groupIndex));
}
/**
* Replaces the group tokens like "$0", "$1", and etc with their actual matches.
*/
public String replace(String s) {
StringBuffer buf = new StringBuffer();
for( int i=0; i<s.length(); i++) {
char ch = s.charAt(i);
if (ch == '\\') {// escape char
i++;
buf.append(s.charAt(i));
} else if (ch == '$') {// replace by group
i++;
ch = s.charAt(i);
// get the group number
int groupId = ch - '0';
if (groupId < 0 || groupId > 9) {
buf.append('$').append(ch);
} else {
// add the group text
String group = group(groupId);
if (group != null)
buf.append(group);
}
} else {
// other chars
buf.append(ch);
}
}
return buf.toString();
}
@Override
protected SubText createSubText(Matcher m) {
return new SubText(m,start);
}
}
public MarkupText(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public void addMarkup( int startPos, int endPos, String startTag, String endTag ) {
rangeCheck(startPos);
rangeCheck(endPos);
if(startPos>endPos) throw new IndexOutOfBoundsException();
// when multiple tags are added to the same range, we want them to show up like
// <b><i>abc</i></b>, not <b><i>abc</b></i>. Also, we'd like <b>abc</b><i>def</i>,
// not <b>abc<i></b>def</i>. Do this by inserting them to different places.
tags.add(new Tag(startPos, startTag));
tags.add(0,new Tag(endPos,endTag));
}
public void addMarkup(int pos, String tag) {
rangeCheck(pos);
tags.add(new Tag(pos,tag));
}
private void rangeCheck(int pos) {
if(pos<0 || pos>text.length())
throw new IndexOutOfBoundsException();
}
/**
* Returns the fully marked-up text.
*/
@Override
public String toString() {
if(tags.isEmpty())
return text; // the most common case
// somewhat inefficient implementation, if there are a lot of mark up and text is large.
Collections.sort(tags);
StringBuilder buf = new StringBuilder();
buf.append(text);
int offset = 0; // remember the # of chars inserted.
for (Tag tag : tags) {
buf.insert(tag.pos+offset,tag.markup);
offset += tag.markup.length();
}
return buf.toString();
}
// perhaps this method doesn't need to be here to remain binary compatible with past versions,
// but having this seems to be safer.
@Override
public List<SubText> findTokens(Pattern pattern) {
return super.findTokens(pattern);
}
@Override
protected SubText createSubText(Matcher m) {
return new SubText(m,0);
}
}