/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.text;
/**
* <p>
* Operations on Strings that contain words.
* </p>
*
* <p>
* This class tries to handle <code>null</code> input gracefully. An exception
* will not be thrown for a <code>null</code> input. Each method documents its
* behavior in more detail.
* </p>
*
* @since 2.0
* @version $Id: WordUtils.java 1436770 2013-01-22 07:09:45Z ggregory $
*/
public class WordUtils {
/**
* <p>
* The {@code line.separator} System Property. Line separator (
* <code>"\n"</code> on UNIX).
* </p>
* <p>
* Defaults to {@code null} if the runtime does not have security access to
* read this property or the property does not exist.
* </p>
* <p>
* This value is initialized when the class is loaded. If
* {@link System#setProperty(String,String)} or
* {@link System#setProperties(java.util.Properties)} is called after this
* class is loaded, the value will be out of sync with that System property.
* </p>
*
* @since Java 1.1
*/
public static final String LINE_SEPARATOR = WordUtils
.getSystemProperty("line.separator");
/**
* <p>
* <code>WordUtils</code> instances should NOT be constructed in standard
* programming. Instead, the class should be used as
* <code>WordUtils.wrap("foo bar", 20);</code>.
* </p>
*
* <p>
* This constructor is public to permit tools that require a JavaBean
* instance to operate.
* </p>
*/
public WordUtils() {
super();
}
// Wrapping
// --------------------------------------------------------------------------
/**
* <p>
* Wraps a single line of text, identifying words by <code>' '</code>.
* </p>
*
* <p>
* New lines will be separated by the system property line separator. Very
* long words, such as URLs will <i>not</i> be wrapped.
* </p>
*
* <p>
* Leading spaces on a new line are stripped. Trailing spaces are not
* stripped.
* </p>
*
* <pre>
* WordUtils.wrap(null, *) = null
* WordUtils.wrap("", *) = ""
* </pre>
*
* @param str
* the String to be word wrapped, may be null
* @param wrapLength
* the column to wrap the words at, less than 1 is treated as 1
* @return a line with newlines inserted, <code>null</code> if null input
*/
public static String wrap(final String str, final int wrapLength) {
return WordUtils.wrap(str, wrapLength, null, false);
}
/**
* <p>
* Wraps a single line of text, identifying words by <code>' '</code>.
* </p>
*
* <p>
* Leading spaces on a new line are stripped. Trailing spaces are not
* stripped.
* </p>
*
* <pre>
* WordUtils.wrap(null, *, *, *) = null
* WordUtils.wrap("", *, *, *) = ""
* </pre>
*
* @param str
* the String to be word wrapped, may be null
* @param wrapLength
* the column to wrap the words at, less than 1 is treated as 1
* @param newLineStr
* the string to insert for a new line, <code>null</code> uses
* the system property line separator
* @param wrapLongWords
* true if long words (such as URLs) should be wrapped
* @return a line with newlines inserted, <code>null</code> if null input
*/
public static String wrap(final String str, final int wrapLength,
final String newLineStr, final boolean wrapLongWords) {
String actualNewLineStr = newLineStr;
int actualWrapLength = wrapLength;
if (str == null) {
return null;
}
if (actualNewLineStr == null) {
actualNewLineStr = WordUtils.LINE_SEPARATOR;
}
if (actualWrapLength < 1) {
actualWrapLength = 1;
}
final int inputLineLength = str.length();
int offset = 0;
final StringBuilder wrappedLine = new StringBuilder(
inputLineLength + 32);
while (inputLineLength - offset > actualWrapLength) {
if (str.charAt(offset) == ' ') {
offset++;
continue;
}
int spaceToWrapAt = str.lastIndexOf(' ', actualWrapLength + offset);
if (spaceToWrapAt >= offset) {
// normal case
wrappedLine.append(str.substring(offset, spaceToWrapAt));
wrappedLine.append(actualNewLineStr);
offset = spaceToWrapAt + 1;
} else {
// really long word or URL
if (wrapLongWords) {
// wrap really long word one line at a time
wrappedLine.append(str.substring(offset, actualWrapLength
+ offset));
wrappedLine.append(actualNewLineStr);
offset += actualWrapLength;
} else {
// do not wrap really long word, just extend beyond limit
spaceToWrapAt = str.indexOf(' ', actualWrapLength + offset);
if (spaceToWrapAt >= 0) {
wrappedLine
.append(str.substring(offset, spaceToWrapAt));
wrappedLine.append(actualNewLineStr);
offset = spaceToWrapAt + 1;
} else {
wrappedLine.append(str.substring(offset));
offset = inputLineLength;
}
}
}
}
// Whatever is left in line is short enough to just pass through
wrappedLine.append(str.substring(offset));
return wrappedLine.toString();
}
/**
* <p>
* Gets a System property, defaulting to {@code null} if the property cannot
* be read.
* </p>
* <p>
* If a {@code SecurityException} is caught, the return value is
* {@code null} and a message is written to {@code System.err}.
* </p>
*
* @param property
* the system property name
* @return the system property value or {@code null} if a security problem
* occurs
*/
private static String getSystemProperty(final String property) {
try {
return System.getProperty(property);
} catch (final SecurityException ex) {
// we are not allowed to look at this property
System.err
.println("Caught a SecurityException reading the system property '"
+ property
+ "'; the SystemUtils property value will default to null.");
return null;
}
}
}