In this method, we make the following translations: *
* & becomes & * " becomes " * < becomes < * > becomes > * newline becomes * carriage return becomes $amp;#13; ** @see #unescapeForXML(String) * * @param string The string to escape. * @return A new string with special characters replaced. */ public static String escapeForXML(String string) { // This method gets called quite a bit when parsing large // files, so rather than calling substitute() many times, // we combine all the loops in one pass. // A different solution might be to scan the string for // escaped xml characters and if any are found, then create a // StringBuffer and do the conversion. Using a profiler would // help here. if (string == null) { return null; } StringBuffer buffer = new StringBuffer(string); int i = 0; int length = string.length(); while (i < length) { switch (buffer.charAt(i)) { case '\n': buffer.deleteCharAt(i); buffer.insert(i, " "); length += 4; break; case '\r': buffer.deleteCharAt(i); buffer.insert(i, " "); length += 4; break; case '"': buffer.deleteCharAt(i); buffer.insert(i, """); length += 5; break; case '&': buffer.deleteCharAt(i); buffer.insert(i, "&"); length += 4; break; case '<': buffer.deleteCharAt(i); buffer.insert(i, "<"); length += 3; break; case '>': buffer.deleteCharAt(i); buffer.insert(i, ">"); length += 3; break; } i++; } return buffer.toString(); } /** Given a string, return a string that when fed to the * Ptolemy expression parser, will turn into the argument * string. That is, replace all the instances of backslashes * with double backslashes, all quotation marks with \", * etc. * For example *
* x"y becomes x\"y; * x\"y becomes x\\\"y; * x\y"z becomes x\\y\"z; * x\\y\"z becomes x\\\\y\\\" ** Similarly, this method replaces the following charaters * exactly as defined in Java strings: \n, \t, \b, \r, and \f. * @param string The string to escape. * @return A new string with that can be put between quotation marks. */ public static String escapeString(String string) { // Since the first string is a regular expression, it needs extra escaping. // I have no idea why the extra escaping is needed on the second argument. string = string.replaceAll("\\\\", "\\\\\\\\"); string = string.replaceAll("\"", "\\\\\""); string = string.replaceAll("\n", "\\\\n"); string = string.replaceAll("\t", "\\\\t"); string = string.replaceAll("\b", "\\\\b"); string = string.replaceAll("\r", "\\\\r"); // Not needed. // string = string.replaceAll("\'", "\\\\'"); return string; } /** If the ptolemy.ptII.exitAfterWrapup or the * ptolemy.ptII.doNotExit properties are not set, then call * System.exit(). * Ptolemy code should call this method instead of directly calling * System.exit() so that we can test code that would usually exit. * @param returnValue The return value of this process, where * non-zero values indicate an error. */ public static void exit(int returnValue) { try { if (StringUtilities.getProperty("ptolemy.ptII.doNotExit").length() > 0) { return; } } catch (SecurityException ex) { System.out.println("Warning: failed to get property \"" + "ptolemy.ptII.doNotExit\". " + "(-sandbox always causes this)"); } try { if (StringUtilities.getProperty("ptolemy.ptII.exitAfterWrapup") .length() > 0) { throw new RuntimeException("Normally, we would " + "exit here because Manager.exitAfterWrapup() " + "was called. However, because the " + "ptolemy.ptII.exitAfterWrapup property " + "is set, we throw this exception instead."); } } catch (SecurityException ex) { System.out.println("Warning: failed to get property \"" + "ptolemy.ptII.exitAfterWrapup\". " + "(-sandbox always causes this)"); } if (!inApplet()) { // Only call System.exit if we are not in an applet. // Non-zero indicates a problem. System.exit(returnValue); } } /** Return a number of spaces that is proportional to the argument. * If the argument is negative or zero, return an empty string. * @param level The level of indenting represented by the spaces. * @return A string with zero or more spaces. */ public static String getIndentPrefix(int level) { if (level <= 0) { return ""; } StringBuffer result = new StringBuffer(level * 4); for (int i = 0; i < level; i++) { result.append(" "); } return result.toString(); } /** Get the specified property from the environment. An empty * string is returned if the property named by the "propertyName" * argument environment variable does not exist, though if * certain properties are not defined, then we make various * attempts to determine them and then set them. See the javadoc * page for java.util.System.getProperties() for a list of system * properties. *
The following properties are handled specially *
c:/foo
whereas most of the other
* methods that operate on path names return
* C:/foo
.
* * "An identifier is an unlimited-length sequence of Java letters * and Java digits, the first of which must be a Java letter. An * identifier cannot have the same spelling (Unicode character * sequence) as a keyword (3.9), boolean literal (3.10.3), or * the null literal (3.10.7)." ** Java characters are A-Z, a-z, $ and _. *
Characters that are not permitted in a Java identifier are changed
* to underscores.
* This method does not check that the returned string is a
* keyword or literal.
* Note that two different strings can sanitize to the same
* string.
* This method is commonly used during code generation to map the
* name of a ptolemy object to a valid identifier name.
* @param name A string with spaces and other characters that
* cannot be in a Java name.
* @return A String that follows the Java identifier rules.
*/
public static String sanitizeName(String name) {
char[] nameArray = name.toCharArray();
for (int i = 0; i < nameArray.length; i++) {
if (!Character.isJavaIdentifierPart(nameArray[i])) {
nameArray[i] = '_';
}
}
if (nameArray.length == 0) {
return "";
} else {
if (!Character.isJavaIdentifierStart(nameArray[0])) {
return "_" + new String(nameArray);
} else {
return new String(nameArray);
}
}
}
/** If the string is longer than 79 characters, split it up by
* adding newlines in all newline delimited substrings
* that are longer than 79 characters.
* If the longName argument is null, then the string
* ">Unnamed<" is returned.
* @see #abbreviate(String)
* @see #split(String, int)
* @param longName The string to optionally split up
* @return Either the original string, or the string with newlines
* inserted.
*/
public static String split(String longName) {
return split(longName, 79);
}
/** If the string is longer than length characters,
* split the string up by adding newlines in all
* newline delimited substrings that are longer than length
* characters.
* If the longName argument is null, then the string
* ">Unnamed<" is returned.
* @see #abbreviate(String)
* @see #split(String)
* @param longName The string to optionally split.
* @param length The maximum length of the sequence of characters
* before a newline is inserted.
* @return Either the original string, or the string with newlines
* inserted.
*/
public static String split(String longName, int length) {
if (longName == null) {
return " If prefix is not a simple prefix of string, then
* we use the file system to find the canonical names of the files.
* For this to work, prefix and string should name
* files that exist, see java.io.File.getCanonicalFile() for details.
*
* If prefix is not a prefix of string, then
* we return string
*
* @param prefix The prefix string, for example, "c:/ptII".
* @param string The string to be substituted, for example,
* "c:/ptII/ptolemy".
* @param replacement The replacement to be substituted in, for example,
* "$PTII"
* @return The possibly substituted string.
*/
public static String substituteFilePrefix(String prefix, String string,
String replacement) {
// This method is currently used by $PTII/util/testsuite/auto.tcl
if (string.startsWith(prefix)) {
// Hmm, what about file separators?
return replacement + string.substring(prefix.length());
} else {
try {
String prefixCanonicalPath = (new File(prefix))
.getCanonicalPath();
String stringCanonicalPath = (new File(string))
.getCanonicalPath();
if (stringCanonicalPath.startsWith(prefixCanonicalPath)) {
return replacement
+ stringCanonicalPath.substring(prefixCanonicalPath
.length());
}
} catch (Throwable throwable) {
// ignore.
}
}
return string;
}
/** Tokenize a String to an array of Strings for use with
* Runtime.exec(String []).
*
* Lines that begin with an octothorpe '#' are ignored.
* Substrings that start and end with a double quote are considered
* to be a single token and are returned as a single array element.
*
* @param inputString The String to tokenize
* @return An array of substrings.
* @exception IOException If StreamTokenizer.nextToken() throws it.
*/
public static String[] tokenizeForExec(String inputString)
throws IOException {
// The java.lang.Runtime.exec(String command) call uses
// java.util.StringTokenizer() to parse the command string.
// Unfortunately, this means that double quotes are not handled
// in the same way that the shell handles them in that 'ls "foo
// 'bar"' will interpreted as three tokens 'ls', '"foo' and
// 'bar"'. In the shell, the string would be two tokens 'ls' and
// '"foo bar"'. What is worse is that the exec() behaviour is
// slightly different under Windows and Unix. To solve this
// problem, we preprocess the command argument using
// java.io.StreamTokenizer, which converts quoted substrings into
// single tokens. We then call java.lang.Runtime.exec(String []
// commands);
// Parse the command into tokens
List commandList = new LinkedList();
StreamTokenizer streamTokenizer = new StreamTokenizer(new StringReader(
inputString));
// We reset the syntax so that we don't convert to numbers,
// otherwise, if PTII is "d:\\tmp\\ptII\ 2.0", then
// we have no end of problems.
streamTokenizer.resetSyntax();
streamTokenizer.whitespaceChars(0, 32);
streamTokenizer.wordChars(33, 127);
// We can't use quoteChar here because it does backslash
// substitution, so "c:\ptII" ends up as "c:ptII"
// Substituting forward slashes for backward slashes seems like
// overkill.
// streamTokenizer.quoteChar('"');
streamTokenizer.ordinaryChar('"');
streamTokenizer.eolIsSignificant(true);
streamTokenizer.commentChar('#');
// Current token
String token = "";
// Single character token, usually a -
String singleToken = "";
// Set to true if we are inside a double quoted String.
boolean inDoubleQuotedString = false;
while (streamTokenizer.nextToken() != StreamTokenizer.TT_EOF) {
switch (streamTokenizer.ttype) {
case StreamTokenizer.TT_WORD:
if (inDoubleQuotedString) {
if (token.length() > 0) {
// FIXME: multiple spaces will get compacted here
token += " ";
}
token += (singleToken + streamTokenizer.sval);
} else {
token = singleToken + streamTokenizer.sval;
commandList.add(token);
}
singleToken = "";
break;
case StreamTokenizer.TT_NUMBER:
throw new RuntimeException("Internal error: Found TT_NUMBER: '"
+ streamTokenizer.nval + "'. We should not be "
+ "tokenizing numbers");
//break;
case StreamTokenizer.TT_EOL:
case StreamTokenizer.TT_EOF:
break;
default:
singleToken = Character.toString((char) streamTokenizer.ttype);
if (singleToken.equals("\"")) {
if (inDoubleQuotedString) {
commandList.add(token);
}
inDoubleQuotedString = !inDoubleQuotedString;
singleToken = "";
token = "";
}
break;
}
}
return (String[]) commandList.toArray(new String[commandList.size()]);
}
/** Return a string with a maximum line length of lineLength
* and a maximum number of lines numberOfLines.
* Each line that exceeds the line length is replaced with a line that
* ends with "...". If the number of lines exceeds numberOfLines,
* then the returned string will have exactly numberOfLines lines
* where the last line is "...".
* @param string The string to truncate.
* @param lineLength The number of characters to which to truncate each line.
* @param numberOfLines The maximum number of lines.
* @return The possibly truncated string with ellipsis possibly added.
*/
public static String truncateString(String string, int lineLength,
int numberOfLines) {
// Third argument being true means the delimiters (LINE_SEPARATOR) are
// included in as tokens in the parsed results.
StringTokenizer tokenizer = new StringTokenizer(string, LINE_SEPARATOR,
true);
StringBuffer results = new StringBuffer();
// Count the lines + newlines.
int lineCount = 0;
while (tokenizer.hasMoreTokens()) {
if (lineCount >= numberOfLines * 2) {
// Presumably, the last line is a line separator.
// We append an additional line to indicate that there
// are more lines.
results.append("...");
break;
}
lineCount++;
String line = tokenizer.nextToken();
if (line.length() > lineLength) {
line = line.substring(0, lineLength - 3) + "...";
}
results.append(line);
}
return results.toString();
}
/** Given a string, replace all the instances of XML entities
* with their corresponding XML special characters. This is necessary to
* allow arbitrary strings to be encoded within XML.
*
* In this method, we make the following translations:
*
* & becomes &
* " becomes "
* < becomes <
* > becomes >
* becomes newline
* becomes carriage return
*
* @see #escapeForXML(String)
*
* @param string The string to escape.
* @return A new string with special characters replaced.
*/
public static String unescapeForXML(String string) {
if (string.indexOf("&") != -1) {
string = substitute(string, "&", "&");
string = substitute(string, """, "\"");
string = substitute(string, "<", "<");
string = substitute(string, ">", ">");
string = substitute(string, "
", "\n");
string = substitute(string, "
", "\r");
}
return string;
}
/** Return a string that contains a description of how to use a
* class that calls this method. For example, this method is
* called by "$PTII/bin/vergil -help".
* @param commandTemplate A string naming the command and the
* format of the arguments, for example
* "moml [options] [file . . .]"
* @param commandOptions A 2xN array of Strings that list command-line
* options that take arguments where the first
* element is a String naming the command line option, and the
* second element is the argument, for example
* {"-class", "
* @param commandFlags An array of Strings that list command-line
* options that are either present or not.
* @return A string that describes the command.
*/
public static String usageString(String commandTemplate,
String[][] commandOptions, String[] commandFlags) {
String[][] commandFlagsWithDescriptions = new String[commandFlags.length][2];
for (int i = 0; i < commandFlags.length; i++) {
commandFlagsWithDescriptions[i][0] = commandFlags[i];
commandFlagsWithDescriptions[i][1] = "";
}
return usageString(commandTemplate, commandOptions,
commandFlagsWithDescriptions);
}
/** Return a string that contains a description of how to use a
* class that calls this method. For example, this method is
* called by "$PTII/bin/vergil -help".
* @param commandTemplate A string naming the command and the
* format of the arguments, for example
* "moml [options] [file . . .]"
* @param commandOptions A 2xN array of Strings that list command-line
* options that take arguments where the first
* element is a String naming the command line option, and the
* second element is the argument, for example
* {"-class", "
* @param commandFlagsWithDescriptions A 2xM array of Strings that list
* command-line options that are either present or not and a description
* of what the command line option does.
* @return A string that describes the command.
*/
public static String usageString(String commandTemplate,
String[][] commandOptions, String[][] commandFlagsWithDescriptions) {
// This method is static so that we can reuse it in places
// like copernicus/kernel/Copernicus and actor/gui/MoMLApplication
StringBuffer result = new StringBuffer("Usage: " + commandTemplate
+ "\n\n" + "Options that take values:\n");
int i;
for (i = 0; i < commandOptions.length; i++) {
result.append(" " + commandOptions[i][0]);
if (commandOptions[i][1].length() > 0) {
result.append(" " + commandOptions[i][1]);
}
result.append("\n");
}
result.append("\nBoolean flags:\n");
for (i = 0; i < commandFlagsWithDescriptions.length; i++) {
result.append(" " + commandFlagsWithDescriptions[i][0]);
if (commandFlagsWithDescriptions[i][1].length() > 0) {
result.append("\t" + commandFlagsWithDescriptions[i][1]);
}
result.append("\n");
}
return result.toString();
}
///////////////////////////////////////////////////////////////////
//// public variables ////
// If you change these, be sure to try running vergil on
// a HSIF moml file
// vergil ../hsif/demo/SwimmingPool/SwimmingPool.xml
/** Maximum length in characters of a long string before
* {@link #ellipsis(String, int)} truncates and add a
* trailing ". . .". This variable is used by callers
* of ellipsis(String, int).
*/
public static final int ELLIPSIS_LENGTH_LONG = 2000;
/** Maximum length in characters of a short string before
* {@link #ellipsis(String, int)} truncates and add a
* trailing ". . .". This variable is used by callers
* of ellipsis(String, int).
*/
public static final int ELLIPSIS_LENGTH_SHORT = 400;
/** The line separator string. Under Windows, this would
* be "\r\n"; under Unix, "\n"; Under Macintosh, "\r".
*/
public static final String LINE_SEPARATOR = System
.getProperty("line.separator");
/** Location of Application preferences such as the user library.
* This field is not final in case other applications want to
* set it to a different directory.
* @see #preferencesDirectory()
*/
public static final String PREFERENCES_DIRECTORY = ".ptolemyII";
///////////////////////////////////////////////////////////////////
//// private variables ////
/** Set to true if we print the cygwin warning in getProperty(). */
private static boolean _printedCygwinWarning = false;
/** Cached value of ptolemy.ptII.dir property. */
private static String _ptolemyPtIIDir = null;
}