001/*
002 * Copyright c 2018 Rusi Popov, MDA Tools.net All rights reserved.
003 *
004 * This program and the accompanying materials are made available under the terms of the
005 * Eclipse Public License v2.0 which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v20.html
007 */
008package net.mdatools.modelant.core.operation.format;
009
010import java.util.StringTokenizer;
011
012import net.mdatools.modelant.core.api.Function;
013
014/**
015 * Wrap the provided text by adding the prefix to all lines. If the text is empty,
016 * defaultVal is returned.
017 *
018 * @author Rusi Popov (popovr@mdatools.net)
019 */
020public class FormatWrapText implements Function<String,String>{
021
022  private static final String SEPARATOR_TAB = "\t";
023  private static final String SEPARATOR_SPACE = " ";
024  private static final String SEPARATORS = SEPARATOR_SPACE+SEPARATOR_TAB;
025
026  /**
027   * OS specific End-Of-Line terminator
028   */
029  private final String lineSeparator;
030
031  /**
032   * The maximum length of a line in JavaDoc comments. After that length the text should be wrapped
033   * on the next line. Guaranteed at least one word in a line.
034   */
035  private final int maxLineLength;
036  private final String prefix;
037  private final String defaultVal;
038
039  /**
040   * Wrap the paragraphs to 105 characters terminated by system-specific line separator
041   * without any prefix and default value.
042   */
043  public FormatWrapText() {
044    this("", "");
045  }
046
047  /**
048   * Wrap the paragraphs to 105 characters terminated by "line.separator"
049   * @param prefix not null is prefix to be inserted in the beginning of each new line
050   * @param defaultVal non null is the value to prefix the first line
051   */
052  public FormatWrapText(String prefix, String defaultVal) {
053    this(prefix, defaultVal, 105);
054  }
055
056  /**
057   * Wrap the paragraphs to maxLineLength characters terminated by "line.separator"
058   * @param prefix not null is prefix to be inserted in the beginning of each new line
059   * @param defaultVal non null is the value to prefix the first line
060   * @param maxLineLength &gt; 0
061   */
062  public FormatWrapText(String prefix, String defaultVal,int maxLineLength) {
063    this(prefix, defaultVal, maxLineLength, System.getProperty( "line.separator" ));
064  }
065
066  /**
067   * Wrap the paragraphs to maxLineLength characters terminated by lineSeparator
068   * @param prefix not null is prefix to be inserted in the beginning of each new line
069   * @param defaultVal non null is the value to prefix the first line
070   * @param maxLineLength &gt; 0
071   * @param lineSeparator not null
072   */
073  public FormatWrapText(String prefix, String defaultVal,int maxLineLength, String lineSeparator) {
074    assert prefix != null : "Expected a non-null prefix provided";
075    assert defaultVal != null : "Expected a non-null default value provided";
076    assert lineSeparator != null : "Expected a non-null line separator provided";
077    assert maxLineLength > 0 : "Expected a positive line length";
078
079    this.prefix = prefix;
080    this.defaultVal = defaultVal;
081    this.maxLineLength = maxLineLength;
082    this.lineSeparator = lineSeparator;
083  }
084
085
086  /**
087   * Adds the prefix to all lines and replaces the line terminators LF or CR LF with the system
088   * specific line terminator (CR LF or LF only). If the line is empty, defaultVal is returned.
089   *
090   * @param line to format
091   * @return a string where all new lines (including the first one) are prefixed with the prefix
092   *         string
093   */
094  public final String execute(String line) {
095    final String result;
096    String paragraph;
097    StringBuilder resultLines;
098    StringTokenizer paragraphs;
099
100    if ( line != null && !line.isEmpty() ) { // there are comments
101      resultLines = new StringBuilder();
102
103      paragraphs = new StringTokenizer(line, "\n\r\f" );
104      while ( paragraphs.hasMoreTokens() ) {
105        paragraph = paragraphs.nextToken();
106
107        wrapParagraph( paragraph, resultLines );
108      }
109      result = resultLines.toString();
110    } else {
111      result = defaultVal;
112    }
113    return result;
114  }
115
116  /**
117   * Wrap the paragraph into the result lines
118   * @param paragraph not null
119   * @param resultLines not null
120   */
121  private void wrapParagraph(String paragraph, StringBuilder resultLines) {
122    String token;
123    StringTokenizer tokens;
124    int linesize;
125
126    linesize = 0;
127    tokens = new StringTokenizer(paragraph, SEPARATORS, true); // keep the separators
128    while ( tokens.hasMoreTokens() ) {
129      token = tokens.nextToken();
130
131      if ( linesize == 0 ) { // new line started
132        if ( !token.equals( SEPARATOR_SPACE ) && !token.equals( SEPARATOR_TAB ) ) { // first real token
133          resultLines.append( prefix );
134          linesize += prefix.length();
135
136          resultLines.append( token );
137          linesize += token.length();
138
139        } // else // parsing a separator char leading the line - do nothing, skip it
140      } else { // not the first token - just collect it
141        resultLines.append( token );
142        linesize += token.length();
143      }
144
145      if ( linesize >= maxLineLength ) { // end of line reached
146        thrimTrailingSeparator( resultLines );
147        resultLines.append(lineSeparator);
148        linesize=0;
149      }
150    }
151    if (linesize>0) { // the last line is incomplete
152      thrimTrailingSeparator( resultLines );
153      resultLines.append(lineSeparator);
154    }
155  }
156
157  /**
158   * Correct the length of the resultLines to trim the trailing separators
159   * @param resultLines
160   */
161  private void thrimTrailingSeparator(StringBuilder resultLines) {
162    while (SEPARATORS.contains(""+resultLines.charAt( resultLines.length()-1 ))) {
163      resultLines.setLength( resultLines.length()-1 );
164    }
165  }
166}