xref: /OpenGrok/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/LineHighlight.java (revision 5d9f3aa0ca3da3a714233f987fa732f62c0965f6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2018, Chris Fraire <cfraire@me.com>.
22  */
23 package org.opengrok.indexer.search.context;
24 
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.List;
28 import org.opengrok.indexer.web.HtmlConsts;
29 import org.opengrok.indexer.web.Util;
30 
31 /**
32  * Represents a collection of metadata related to highlighting a single line
33  * of code.
34  */
35 public class LineHighlight {
36 
37     private final int lineno;
38     private List<PhraseHighlight> markups;
39     /** Offset of elided left part. */
40     private int lelide;
41     /** Offset of elide right part. */
42     private int relide;
43 
44     private boolean didLelide;
45     private boolean didRelide;
46 
LineHighlight(int lineno)47     public LineHighlight(int lineno) {
48         if (lineno < 0) {
49             throw new IllegalArgumentException("lineno cannot be negative");
50         }
51         this.lineno = lineno;
52     }
53 
54     /**
55      * Gets the number of markups.
56      * @return zero or greater
57      */
countMarkups()58     public int countMarkups() {
59         return markups == null ? 0 : markups.size();
60     }
61 
62     /**
63      * Gets the highlight at the specified position.
64      * @param i index of element to return
65      * @return defined instance
66      */
getMarkup(int i)67     public PhraseHighlight getMarkup(int i) {
68         return markups.get(i);
69     }
70 
71     /**
72      * Sort and condense overlapping markup highlights.
73      */
condenseMarkups()74     public void condenseMarkups() {
75         if (markups == null) {
76             return;
77         }
78 
79         markups.sort(PhraseHighlightComparator.INSTANCE);
80         // Condense instances if there is overlap.
81         for (int i = 0; i + 1 < markups.size(); ++i) {
82             PhraseHighlight phi0 = markups.get(i);
83             PhraseHighlight phi1 = markups.get(i + 1);
84             if (phi0.overlaps(phi1)) {
85                 phi0 = phi0.merge(phi1);
86                 markups.set(i, phi0);
87                 markups.remove(i + 1);
88                 --i;
89             }
90         }
91     }
92 
93     /**
94      * Adds the specified highlight.
95      * @param phi a defined instance
96      */
addMarkup(PhraseHighlight phi)97     public void addMarkup(PhraseHighlight phi) {
98         if (phi == null) {
99             throw new IllegalArgumentException("phi is null");
100         }
101         if (markups == null) {
102             markups = new ArrayList<>();
103         }
104         markups.add(phi);
105     }
106 
107     /**
108      * @return the lineno
109      */
getLineno()110     public int getLineno() {
111         return lineno;
112     }
113 
114     /**
115      * Gets the left elide value.
116      * @return zero or greater
117      */
getLelide()118     public int getLelide() {
119         return lelide;
120     }
121 
122     /**
123      * Sets the left elide value.
124      * @param value integer value
125      */
setLelide(int value)126     public void setLelide(int value) {
127         if (value < 0) {
128             throw new IllegalArgumentException("value is negative");
129         }
130         this.lelide = value;
131     }
132 
133     /**
134      * Gets the right elide value.
135      * @return zero or greater
136      */
getRelide()137     public int getRelide() {
138         return relide;
139     }
140 
141     /**
142      * Sets the right elide value.
143      * @param value integer value
144      */
setRelide(int value)145     public void setRelide(int value) {
146         if (value < 0) {
147             throw new IllegalArgumentException("value is negative");
148         }
149         this.relide = value;
150     }
151 
152     /**
153      * Append a substring with
154      * {@link Util#htmlize(java.lang.CharSequence, java.lang.Appendable, boolean)},
155      * taking into account any positive {@link #getLelide()} or
156      * {@link #getRelide()}.
157      * @param dest appendable object
158      * @param line line value
159      * @param start start offset
160      * @param end end offset
161      * @throws IOException I/O
162      */
hsub(Appendable dest, String line, int start, int end)163     public void hsub(Appendable dest, String line, int start, int end)
164             throws IOException {
165         boolean lell = false;
166         boolean rell = false;
167         if (start < lelide) {
168             lell = true;
169             start = lelide;
170         }
171         if (end < lelide) {
172             end = lelide;
173         }
174         if (relide > 0) {
175             if (start > relide) {
176                 start = relide;
177             }
178             if (end > relide) {
179                 rell = true;
180                 end = relide;
181             }
182         }
183         String str = line.substring(start, end);
184         if (lell && !didLelide) {
185             dest.append(HtmlConsts.HELLIP);
186             didLelide = true;
187         }
188         Util.htmlize(str, dest, true);
189         if (rell && !didRelide) {
190             dest.append(HtmlConsts.HELLIP);
191             didRelide = true;
192         }
193     }
194 
195     /**
196      * Calls {@link #hsub(java.lang.Appendable, java.lang.String, int, int)}
197      * with {@code dest}, {@code line}, {@code loff}, and {@code line}
198      * {@link String#length()}.
199      * @param dest appendable object
200      * @param line line value
201      * @param loff start offset
202      * @throws IOException I/O
203      */
hsub(Appendable dest, String line, int loff)204     public void hsub(Appendable dest, String line, int loff)
205             throws IOException {
206         hsub(dest, line, loff, line.length());
207     }
208 }
209