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