1b5840353SAdam Hornáček /* 2b5840353SAdam Hornáček * CDDL HEADER START 3b5840353SAdam Hornáček * 4b5840353SAdam Hornáček * The contents of this file are subject to the terms of the 5b5840353SAdam Hornáček * Common Development and Distribution License (the "License"). 6b5840353SAdam Hornáček * You may not use this file except in compliance with the License. 7b5840353SAdam Hornáček * 8b5840353SAdam Hornáček * See LICENSE.txt included in this distribution for the specific 9b5840353SAdam Hornáček * language governing permissions and limitations under the License. 10b5840353SAdam Hornáček * 11b5840353SAdam Hornáček * When distributing Covered Code, include this CDDL HEADER in each 12b5840353SAdam Hornáček * file and include the License file at LICENSE.txt. 13b5840353SAdam Hornáček * If applicable, add the following below this CDDL HEADER, with the 14b5840353SAdam Hornáček * fields enclosed by brackets "[]" replaced with your own identifying 15b5840353SAdam Hornáček * information: Portions Copyright [yyyy] [name of copyright owner] 16b5840353SAdam Hornáček * 17b5840353SAdam Hornáček * CDDL HEADER END 18b5840353SAdam Hornáček */ 19b5840353SAdam Hornáček 20b5840353SAdam Hornáček /* 21c6f0939bSAdam Hornacek * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. 225d9f3aa0SAdam Hornáček * Portions Copyright (c) 2011, Jens Elkner. 235d9f3aa0SAdam Hornáček * Portions Copyright (c) 2017, 2020, Chris Fraire <cfraire@me.com>. 24b5840353SAdam Hornáček */ 259805b761SAdam Hornáček package org.opengrok.indexer.search; 26b5840353SAdam Hornáček 2757eefa47SKryštof Tulinger import static org.opengrok.indexer.web.messages.MessagesContainer.MESSAGES_MAIN_PAGE_TAG; 2857eefa47SKryštof Tulinger 29b5840353SAdam Hornáček import java.io.BufferedReader; 30b5840353SAdam Hornáček import java.io.File; 31b5840353SAdam Hornáček import java.io.FileInputStream; 32b5840353SAdam Hornáček import java.io.IOException; 33b5840353SAdam Hornáček import java.io.Reader; 34b5840353SAdam Hornáček import java.io.Writer; 35b5840353SAdam Hornáček import java.nio.charset.StandardCharsets; 36b5840353SAdam Hornáček import java.text.DateFormat; 37b5840353SAdam Hornáček import java.text.ParseException; 38b5840353SAdam Hornáček import java.util.ArrayList; 39b5840353SAdam Hornáček import java.util.LinkedHashMap; 40b5840353SAdam Hornáček import java.util.Map; 41b5840353SAdam Hornáček import java.util.logging.Level; 42b5840353SAdam Hornáček import java.util.logging.Logger; 43b5840353SAdam Hornáček import java.util.zip.GZIPInputStream; 44b5840353SAdam Hornáček import org.apache.lucene.analysis.charfilter.HTMLStripCharFilter; 45b5840353SAdam Hornáček import org.apache.lucene.document.DateTools; 46b5840353SAdam Hornáček import org.apache.lucene.document.Document; 47b5840353SAdam Hornáček import org.apache.lucene.index.CorruptIndexException; 48b5840353SAdam Hornáček import org.apache.lucene.index.IndexableField; 49b5840353SAdam Hornáček import org.apache.lucene.search.IndexSearcher; 50b5840353SAdam Hornáček import org.apache.lucene.search.ScoreDoc; 5157eefa47SKryštof Tulinger import org.opengrok.indexer.analysis.AbstractAnalyzer; 529805b761SAdam Hornáček import org.opengrok.indexer.analysis.Definitions; 539805b761SAdam Hornáček import org.opengrok.indexer.analysis.Scopes; 549805b761SAdam Hornáček import org.opengrok.indexer.configuration.Project; 559805b761SAdam Hornáček import org.opengrok.indexer.configuration.RuntimeEnvironment; 569805b761SAdam Hornáček import org.opengrok.indexer.history.HistoryException; 579805b761SAdam Hornáček import org.opengrok.indexer.logger.LoggerFactory; 58ae1c323bSVladimir Kotal import org.opengrok.indexer.search.context.HistoryContext; 599805b761SAdam Hornáček import org.opengrok.indexer.util.IOUtils; 604da26a1eSChris Fraire import org.opengrok.indexer.util.TandemPath; 619805b761SAdam Hornáček import org.opengrok.indexer.web.Prefix; 629805b761SAdam Hornáček import org.opengrok.indexer.web.SearchHelper; 639805b761SAdam Hornáček import org.opengrok.indexer.web.Util; 643db691beSVladimir Kotal import org.opengrok.indexer.web.messages.MessagesUtils; 653db691beSVladimir Kotal 66b5840353SAdam Hornáček /** 67b5840353SAdam Hornáček * @author Chandan slightly rewritten by Lubos Kosco 68b5840353SAdam Hornáček */ 69b5840353SAdam Hornáček public final class Results { 70b5840353SAdam Hornáček 71b5840353SAdam Hornáček private static final Logger LOGGER = LoggerFactory.getLogger(Results.class); 72b5840353SAdam Hornáček Results()73b5840353SAdam Hornáček private Results() { 74b5840353SAdam Hornáček // Util class, should not be constructed 75b5840353SAdam Hornáček } 76b5840353SAdam Hornáček 77b5840353SAdam Hornáček /** 78b5840353SAdam Hornáček * Create a has map keyed by the directory of the document found. 79b5840353SAdam Hornáček * 80b5840353SAdam Hornáček * @param searcher searcher to use. 81b5840353SAdam Hornáček * @param hits hits produced by the given searcher's search 82b5840353SAdam Hornáček * @param startIdx the index of the first hit to check 83b5840353SAdam Hornáček * @param stopIdx the index of the last hit to check 84b5840353SAdam Hornáček * @return a (directory, hitDocument) hashmap 85b5840353SAdam Hornáček * @throws CorruptIndexException 86b5840353SAdam Hornáček * @throws IOException 87b5840353SAdam Hornáček */ createMap( IndexSearcher searcher, ScoreDoc[] hits, int startIdx, long stopIdx)88b5840353SAdam Hornáček private static Map<String, ArrayList<Integer>> createMap( 89b5840353SAdam Hornáček IndexSearcher searcher, ScoreDoc[] hits, int startIdx, long stopIdx) 90b5840353SAdam Hornáček throws CorruptIndexException, IOException { 91b5840353SAdam Hornáček 92b5840353SAdam Hornáček LinkedHashMap<String, ArrayList<Integer>> dirHash = 93b5840353SAdam Hornáček new LinkedHashMap<>(); 94b5840353SAdam Hornáček for (int i = startIdx; i < stopIdx; i++) { 95b5840353SAdam Hornáček int docId = hits[i].doc; 96b5840353SAdam Hornáček Document doc = searcher.doc(docId); 97b5840353SAdam Hornáček 98b5840353SAdam Hornáček String rpath = doc.get(QueryBuilder.PATH); 99b5840353SAdam Hornáček if (rpath == null) { 100b5840353SAdam Hornáček continue; 101b5840353SAdam Hornáček } 102b5840353SAdam Hornáček 103b5840353SAdam Hornáček String parent = rpath.substring(0, rpath.lastIndexOf('/')); 104c6f0939bSAdam Hornacek ArrayList<Integer> dirDocs = dirHash.computeIfAbsent(parent, k -> new ArrayList<>()); 105b5840353SAdam Hornáček dirDocs.add(docId); 106b5840353SAdam Hornáček } 107b5840353SAdam Hornáček return dirHash; 108b5840353SAdam Hornáček } 109b5840353SAdam Hornáček getTags(File basedir, String path, boolean compressed)110b5840353SAdam Hornáček private static String getTags(File basedir, String path, boolean compressed) { 111b5840353SAdam Hornáček char[] content = new char[1024 * 8]; 112b5840353SAdam Hornáček try (HTMLStripCharFilter r = new HTMLStripCharFilter(getXrefReader(basedir, path, compressed))) { 113b5840353SAdam Hornáček int len = r.read(content); 114b5840353SAdam Hornáček return new String(content, 0, len); 115b5840353SAdam Hornáček } catch (Exception e) { 1164da26a1eSChris Fraire String fnm = compressed ? TandemPath.join(basedir + path, ".gz") : 1174da26a1eSChris Fraire basedir + path; 1184da26a1eSChris Fraire LOGGER.log(Level.WARNING, "An error reading tags from " + fnm, e); 119b5840353SAdam Hornáček } 120b5840353SAdam Hornáček return ""; 121b5840353SAdam Hornáček } 122b5840353SAdam Hornáček 123b5840353SAdam Hornáček /** Return a reader for the specified xref file. */ getXrefReader( File basedir, String path, boolean compressed)124b5840353SAdam Hornáček private static Reader getXrefReader( 125b5840353SAdam Hornáček File basedir, String path, boolean compressed) 126b5840353SAdam Hornáček throws IOException { 1274da26a1eSChris Fraire /* 128b5840353SAdam Hornáček * For backward compatibility, read the OpenGrok-produced document 129b5840353SAdam Hornáček * using the system default charset. 130b5840353SAdam Hornáček */ 131b5840353SAdam Hornáček if (compressed) { 132b5840353SAdam Hornáček return new BufferedReader(IOUtils.createBOMStrippedReader( 1334da26a1eSChris Fraire new GZIPInputStream(new FileInputStream(new File(basedir, 1344da26a1eSChris Fraire TandemPath.join(path, ".gz")))))); 135b5840353SAdam Hornáček } else { 136b5840353SAdam Hornáček return new BufferedReader(IOUtils.createBOMStrippedReader( 137b5840353SAdam Hornáček new FileInputStream(new File(basedir, path)))); 138b5840353SAdam Hornáček } 139b5840353SAdam Hornáček } 140b5840353SAdam Hornáček 141b5840353SAdam Hornáček /** 142b5840353SAdam Hornáček * Prints out results in html form. The following search helper fields are 143b5840353SAdam Hornáček * required to be properly initialized: <ul> 144b5840353SAdam Hornáček * <li>{@link SearchHelper#dataRoot}</li> 145b5840353SAdam Hornáček * <li>{@link SearchHelper#contextPath}</li> 146b5840353SAdam Hornáček * <li>{@link SearchHelper#searcher}</li> <li>{@link SearchHelper#hits}</li> 147b5840353SAdam Hornáček * <li>{@link SearchHelper#historyContext} (ignored if {@code null})</li> 148b5840353SAdam Hornáček * <li>{@link SearchHelper#sourceContext} (ignored if {@code null})</li> 149b5840353SAdam Hornáček * <li>{@link SearchHelper#summarizer} (if sourceContext is not 150384ec09dSChris Fraire * {@code null})</li> <li>{@link SearchHelper#sourceRoot} (if 151b5840353SAdam Hornáček * sourceContext or historyContext is not {@code null})</li> </ul> 152b5840353SAdam Hornáček * 153b5840353SAdam Hornáček * @param out write destination 154b5840353SAdam Hornáček * @param sh search helper which has all required fields set 155b5840353SAdam Hornáček * @param start index of the first hit to print 156b5840353SAdam Hornáček * @param end index of the last hit to print 15781b586e6SVladimir Kotal * @throws HistoryException history exception 15881b586e6SVladimir Kotal * @throws IOException I/O exception 15981b586e6SVladimir Kotal * @throws ClassNotFoundException class not found 160b5840353SAdam Hornáček */ prettyPrint(Writer out, SearchHelper sh, int start, long end)161b5840353SAdam Hornáček public static void prettyPrint(Writer out, SearchHelper sh, int start, 162b5840353SAdam Hornáček long end) 163b5840353SAdam Hornáček throws HistoryException, IOException, ClassNotFoundException { 164b5840353SAdam Hornáček Project p; 165ae1c323bSVladimir Kotal String contextPath = sh.getContextPath(); 166*d6df19e1SAdam Hornacek String ctxE = Util.uriEncodePath(contextPath); 167ae1c323bSVladimir Kotal String xrefPrefix = contextPath + Prefix.XREF_P; 168ae1c323bSVladimir Kotal String morePrefix = contextPath + Prefix.MORE_P; 169b5840353SAdam Hornáček String xrefPrefixE = ctxE + Prefix.XREF_P; 170ae1c323bSVladimir Kotal File xrefDataDir = new File(sh.getDataRoot(), Prefix.XREF_P.toString()); 171b5840353SAdam Hornáček 172b5840353SAdam Hornáček RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 173b5840353SAdam Hornáček 174b5840353SAdam Hornáček boolean evenRow = true; 175b5840353SAdam Hornáček out.write("<tbody class=\"search-result\">"); 176b5840353SAdam Hornáček for (Map.Entry<String, ArrayList<Integer>> entry : 177ae1c323bSVladimir Kotal createMap(sh.getSearcher(), sh.getHits(), start, end).entrySet()) { 178b5840353SAdam Hornáček String parent = entry.getKey(); 179b5840353SAdam Hornáček out.write("<tr class=\"dir\"><td colspan=\"3\"><a href=\""); 180b5840353SAdam Hornáček out.write(xrefPrefixE); 181*d6df19e1SAdam Hornacek out.write(Util.uriEncodePath(parent)); 182b5840353SAdam Hornáček out.write("/\">"); 183b5840353SAdam Hornáček out.write(htmlize(parent)); 184b5840353SAdam Hornáček out.write("/</a>"); 185ae1c323bSVladimir Kotal if (sh.getDesc() != null) { 186b5840353SAdam Hornáček out.write(" - <i>"); 187ae1c323bSVladimir Kotal out.write(sh.getDesc().get(parent)); 188b5840353SAdam Hornáček out.write("</i>"); 189b5840353SAdam Hornáček } 1903db691beSVladimir Kotal 1913db691beSVladimir Kotal p = Project.getProject(parent); 1923db691beSVladimir Kotal String messages = MessagesUtils.messagesToJson(p, MESSAGES_MAIN_PAGE_TAG); 1933db691beSVladimir Kotal if (p != null && !messages.isEmpty()) { 194b5840353SAdam Hornáček out.write(" <a href=\"" + xrefPrefix + "/" + p.getName() + "\">"); 19557396dcbSVladimir Kotal out.write("<span class=\"note-" + MessagesUtils.getMessageLevel(p.getName(), MESSAGES_MAIN_PAGE_TAG) + 19657396dcbSVladimir Kotal " important-note important-note-rounded\" data-messages='" + messages + "'>!</span>"); 197b5840353SAdam Hornáček out.write("</a>"); 198b5840353SAdam Hornáček } 199b5840353SAdam Hornáček 200b5840353SAdam Hornáček int tabSize = sh.getTabSize(p); 201b5840353SAdam Hornáček PrintPlainFinalArgs fargs = new PrintPlainFinalArgs(out, sh, env, 202b5840353SAdam Hornáček xrefPrefix, tabSize, morePrefix); 203b5840353SAdam Hornáček 204b5840353SAdam Hornáček out.write("</td></tr>"); 205b5840353SAdam Hornáček for (int docId : entry.getValue()) { 206ae1c323bSVladimir Kotal Document doc = sh.getSearcher().doc(docId); 207b5840353SAdam Hornáček String rpath = doc.get(QueryBuilder.PATH); 208*d6df19e1SAdam Hornacek String rpathE = Util.uriEncodePath(rpath); 209b5840353SAdam Hornáček if (evenRow) { 210b5840353SAdam Hornáček out.write("<tr class=\"search-result-even-row\">"); 211b5840353SAdam Hornáček } else { 212b5840353SAdam Hornáček out.write("<tr>"); 213b5840353SAdam Hornáček } 214b5840353SAdam Hornáček evenRow = !evenRow; 215ae1c323bSVladimir Kotal Util.writeHAD(out, sh.getContextPath(), rpathE, false); 216b5840353SAdam Hornáček out.write("<td class=\"f\"><a href=\""); 217b5840353SAdam Hornáček out.write(xrefPrefixE); 218b5840353SAdam Hornáček out.write(rpathE); 219b5840353SAdam Hornáček out.write("\""); 220b5840353SAdam Hornáček if (env.isLastEditedDisplayMode()) { 221b5840353SAdam Hornáček printLastEditedDate(out, doc); 222b5840353SAdam Hornáček } 223b5840353SAdam Hornáček out.write(">"); 224b5840353SAdam Hornáček out.write(htmlize(rpath.substring(rpath.lastIndexOf('/') + 1))); 225b5840353SAdam Hornáček out.write("</a>"); 226b5840353SAdam Hornáček out.write("</td><td><code class=\"con\">"); 227ae1c323bSVladimir Kotal if (sh.getSourceContext() != null) { 2281c830032SChris Fraire AbstractAnalyzer.Genre genre = AbstractAnalyzer.Genre.get( 2291c830032SChris Fraire doc.get(QueryBuilder.T)); 230ae1c323bSVladimir Kotal Summarizer summarizer = sh.getSummarizer(); 231ae1c323bSVladimir Kotal if (AbstractAnalyzer.Genre.XREFABLE == genre && summarizer != null) { 23253377eb8SChris Fraire String xtags = getTags(xrefDataDir, rpath, env.isCompressXref()); 233b5840353SAdam Hornáček // FIXME use Highlighter from lucene contrib here, 234b5840353SAdam Hornáček // instead of summarizer, we'd also get rid of 235b5840353SAdam Hornáček // apache lucene in whole source ... 236ae1c323bSVladimir Kotal out.write(summarizer.getSummary(xtags).toString()); 237ae1c323bSVladimir Kotal } else if (AbstractAnalyzer.Genre.HTML == genre && summarizer != null) { 238ae1c323bSVladimir Kotal String htags = getTags(sh.getSourceRoot(), rpath, false); 239ae1c323bSVladimir Kotal out.write(summarizer.getSummary(htags).toString()); 24057eefa47SKryštof Tulinger } else if (genre == AbstractAnalyzer.Genre.PLAIN) { 241b5840353SAdam Hornáček printPlain(fargs, doc, docId, rpath); 242b5840353SAdam Hornáček } 243b5840353SAdam Hornáček } 244b5840353SAdam Hornáček 245ae1c323bSVladimir Kotal HistoryContext historyContext = sh.getHistoryContext(); 246ae1c323bSVladimir Kotal if (historyContext != null) { 247ae1c323bSVladimir Kotal historyContext.getContext(new File(sh.getSourceRoot(), rpath), 248ae1c323bSVladimir Kotal rpath, out, sh.getContextPath()); 249b5840353SAdam Hornáček } 250b5840353SAdam Hornáček out.write("</code></td></tr>\n"); 251b5840353SAdam Hornáček } 252b5840353SAdam Hornáček } 253b5840353SAdam Hornáček out.write("</tbody>"); 254b5840353SAdam Hornáček } 255b5840353SAdam Hornáček printLastEditedDate(final Writer out, final Document doc)256b5840353SAdam Hornáček private static void printLastEditedDate(final Writer out, final Document doc) throws IOException { 257b5840353SAdam Hornáček try { 258b5840353SAdam Hornáček DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); 259b5840353SAdam Hornáček String dd = df.format(DateTools.stringToDate(doc.get("date"))); 260b5840353SAdam Hornáček out.write(" class=\"result-annotate\" title=\""); 261b5840353SAdam Hornáček out.write("Last modified: "); 262b5840353SAdam Hornáček out.write(dd); 263b5840353SAdam Hornáček out.write("\""); 264b5840353SAdam Hornáček } catch (ParseException ex) { 265b5840353SAdam Hornáček LOGGER.log(Level.WARNING, "An error parsing date information", ex); 266b5840353SAdam Hornáček } 267b5840353SAdam Hornáček } 268b5840353SAdam Hornáček printPlain(PrintPlainFinalArgs fargs, Document doc, int docId, String rpath)269b5840353SAdam Hornáček private static void printPlain(PrintPlainFinalArgs fargs, Document doc, 270b5840353SAdam Hornáček int docId, String rpath) throws ClassNotFoundException, IOException { 271b5840353SAdam Hornáček 272ae1c323bSVladimir Kotal fargs.shelp.getSourceContext().toggleAlt(); 273b5840353SAdam Hornáček 274ae1c323bSVladimir Kotal boolean didPresentNew = fargs.shelp.getSourceContext().getContext2(fargs.env, 275ae1c323bSVladimir Kotal fargs.shelp.getSearcher(), docId, fargs.out, fargs.xrefPrefix, 276b5840353SAdam Hornáček fargs.morePrefix, true, fargs.tabSize); 277b5840353SAdam Hornáček 278b5840353SAdam Hornáček if (!didPresentNew) { 279174f246bSChris Fraire /* 280b5840353SAdam Hornáček * Fall back to the old view, which re-analyzes text using 281b5840353SAdam Hornáček * PlainLinetokenizer. E.g., when source code is updated (thus 282b5840353SAdam Hornáček * affecting timestamps) but re-indexing is not yet complete. 283b5840353SAdam Hornáček */ 284b5840353SAdam Hornáček Definitions tags = null; 285b5840353SAdam Hornáček IndexableField tagsField = doc.getField(QueryBuilder.TAGS); 286b5840353SAdam Hornáček if (tagsField != null) { 287b5840353SAdam Hornáček tags = Definitions.deserialize(tagsField.binaryValue().bytes); 288b5840353SAdam Hornáček } 289b5840353SAdam Hornáček Scopes scopes; 290b5840353SAdam Hornáček IndexableField scopesField = doc.getField(QueryBuilder.SCOPES); 291b5840353SAdam Hornáček if (scopesField != null) { 292b5840353SAdam Hornáček scopes = Scopes.deserialize(scopesField.binaryValue().bytes); 293b5840353SAdam Hornáček } else { 294b5840353SAdam Hornáček scopes = new Scopes(); 295b5840353SAdam Hornáček } 296ae1c323bSVladimir Kotal boolean isDefSearch = fargs.shelp.getBuilder().isDefSearch(); 297b5840353SAdam Hornáček // SRCROOT is read with UTF-8 as a default. 298ae1c323bSVladimir Kotal File sourceFile = new File(fargs.shelp.getSourceRoot(), rpath); 2994b613dedSAdam Hornacek try (FileInputStream fis = new FileInputStream(sourceFile); 3004b613dedSAdam Hornacek Reader r = IOUtils.createBOMStrippedReader(fis, StandardCharsets.UTF_8.name())) { 301ae1c323bSVladimir Kotal fargs.shelp.getSourceContext().getContext(r, fargs.out, 302b5840353SAdam Hornáček fargs.xrefPrefix, fargs.morePrefix, rpath, tags, true, 303b5840353SAdam Hornáček isDefSearch, null, scopes); 304174f246bSChris Fraire } catch (IOException ex) { 30530bba29fSChris Fraire String errMsg = String.format("No context for %s", sourceFile); 30630bba29fSChris Fraire if (LOGGER.isLoggable(Level.FINE)) { 30730bba29fSChris Fraire // WARNING but with FINE detail 30830bba29fSChris Fraire LOGGER.log(Level.WARNING, errMsg, ex); 30930bba29fSChris Fraire } else { 31030bba29fSChris Fraire LOGGER.log(Level.WARNING, errMsg); 31130bba29fSChris Fraire } 312b5840353SAdam Hornáček } 313b5840353SAdam Hornáček } 314b5840353SAdam Hornáček } 315b5840353SAdam Hornáček htmlize(String raw)316b5840353SAdam Hornáček private static String htmlize(String raw) { 317b5840353SAdam Hornáček return Util.htmlize(raw); 318b5840353SAdam Hornáček } 319b5840353SAdam Hornáček 320b5840353SAdam Hornáček private static class PrintPlainFinalArgs { 321b5840353SAdam Hornáček final Writer out; 322b5840353SAdam Hornáček final SearchHelper shelp; 323b5840353SAdam Hornáček final RuntimeEnvironment env; 324b5840353SAdam Hornáček final String xrefPrefix; 325b5840353SAdam Hornáček final String morePrefix; 326b5840353SAdam Hornáček final int tabSize; 327b5840353SAdam Hornáček PrintPlainFinalArgs(Writer out, SearchHelper shelp, RuntimeEnvironment env, String xrefPrefix, int tabSize, String morePrefix)328d1e826faSAdam Hornáček PrintPlainFinalArgs(Writer out, SearchHelper shelp, 329b5840353SAdam Hornáček RuntimeEnvironment env, String xrefPrefix, int tabSize, 330b5840353SAdam Hornáček String morePrefix) { 331b5840353SAdam Hornáček this.out = out; 332b5840353SAdam Hornáček this.shelp = shelp; 333b5840353SAdam Hornáček this.env = env; 334b5840353SAdam Hornáček this.xrefPrefix = xrefPrefix; 335b5840353SAdam Hornáček this.morePrefix = morePrefix; 336b5840353SAdam Hornáček this.tabSize = tabSize; 337b5840353SAdam Hornáček } 338b5840353SAdam Hornáček } 339b5840353SAdam Hornáček } 340