/*
* [Tools.java]
*
* Summary: common tools for specific HTML macros, also configuring constants.
*
* Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
*
* Licence: This software may be copied and used freely for any purpose but military.
* http://mindprod.com/contact/nonmil.html
*
* Requires: JDK 1.8+
*
* Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
*
* Version History:
* 1.8 2009-02-06 include go package in ZIP bundle.
* 1.9 2014-09-28 replace methods with EIO.getExtension and EIO.getCoreName
*/
package com.mindprod.htmlmacros.support;
import com.mindprod.common18.EIO;
import com.mindprod.common18.ST;
import com.mindprod.entities.DeEntifyStrings;
import com.mindprod.fastcat.FastCat;
import com.mindprod.htmlmacros.macro.Global;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.regex.Pattern;
import static com.mindprod.htmlmacros.macro.Global.configuration;
import static java.lang.System.*;
/**
* common tools for specific HTML macros, also configuring constants.
*
* @author Roedy Green, Canadian Mind Products
* @version 1.9 2014-09-28 replace methods with EIO.getExtension and EIO.getCoreName
* @see BuildImage
* @since 2009
*/
@SuppressWarnings( { "UnusedDeclaration" } )
public final class Tools
{
// declarations
/**
* true if we are debugging. Gets extra output
*/
private static final boolean DEBUGGING = false;
/**
* Pattern to split path into directory segments separated by /
*/
private static final Pattern SPLIT_ON_SLASH = Pattern.compile( "/" );
/**
* webroot name with lead E:/ as file
*/
static File rootDir = new File( configuration.getLocalWebrootWithSlashes().toLowerCase() );
/**
* webroot name with lead E:\
*/
private static String rootDirNameWithBackSlashes = configuration.getLocalWebrootWithBackslashes().toLowerCase();
/**
* webroot name with lead E:/
*/
private static String rootDirNameWithSlashes = configuration.getLocalWebrootWithSlashes().toLowerCase();
// /declarations
// methods
/**
* Used by completeLink
* expandNoRef expandNoRef to a full link
* Used to help implement cookedLinkWithSlashes. Handles absolute URL case.
*
* @param absoluteURL URL starting with http://
* @param description text description of link.
* @param linkCSSClass css class of the link. null will be treated as offsite
* @param target which frame to use e.g "_blank"
*
* @return href to that link
* @see #relativeURL(String, java.io.File)
*/
@SuppressWarnings( { "OverloadedMethodsWithSameNumberOfParameters" } )
private static String completeOffsiteLink( String absoluteURL,
String description,
@Nullable String linkCSSClass,
final String target )
{
if ( linkCSSClass == null || linkCSSClass.length() == 0 )
{
linkCSSClass = Global.assignCSSClasses.assignCSSClass( absoluteURL, null );
}
final FastCat sb = new FastCat( 12 );
sb.append( " 0 && !configuration.isUsingXHTML() )
{
sb.append( "target=\"" );
sb.append( target );
sb.append( "\" " );
}
if ( linkCSSClass.length() > 0 )
{
sb.append( "class=\"" );
sb.append( linkCSSClass );
sb.append( "\" " );
}
sb.append( "href=\"" );
sb.append( absoluteURL );
sb.append( "\">" );
sb.append( description );
sb.append( "" );
return sb.toString();
} // /methods
/**
* Get the pieces of the directory name, webroot relative.
*
* @param dir directory name, not fileBeingDistributed Get array of directory segments that make up the directory,
* possibly empty. livinglove/methods gives { "livinglove" "methods" }
*
* @return just segments of directory name.
*/
@SuppressWarnings( { "WeakerAccess" } )
private static String[] dirSegments( String dir )
{
if ( dir == null || dir.length() == 0 )
{
// split would return one empty element
// in this case instead of empty array
return new String[ 0 ];
}
else
{
if ( dir.indexOf( '/' ) < 0 )
{
// faster than splitter, the normal case
return new String[] { dir };
}
else
{
// two or more directory segments
return SPLIT_ON_SLASH.split( dir );
}
}
} // /methods
/**
* Get string representing how deeply nested we are from the root.
*
* @param level depth we are from webRoot.
*
* @return empty for root, ../ for one deep, ../../ for two deep
* @see #relativeURL
*/
private static String getLevelString( int level )
{
if ( level < 0 )
{
throw new IllegalArgumentException(
"Tools.getLevelString: invalid directory nesting level "
+ level
);
}
switch ( level )
{
case 0:
return "";
case 1:
return "../";
case 2:
return "../../";
default:
final FastCat sb = new FastCat( level );
for ( int i = 0; i < level; i++ )
{
sb.append( "../" );
}
return sb.toString();
} // end switch
} // /methods
/**
* Test link method to generate relative references
*
* @param fromFilename filename relative to webRoot
* @param toFilename toFilename relative to webRoot
*/
private static void linkTest( String fromFilename, String toFilename )
{
if ( DEBUGGING )
{
final File fromFile = new File( rootDir, fromFilename );
out.println( "link test: from: ["
+ fromFilename
+ "] to: ["
+ toFilename
+ "] relative link: ["
+ relativeURL( toFilename, fromFile )
+ "]" );
}
} // /methods
/**
* Excercise the methods on a given filename relative to the webRoot.
*
* @param filename webRoot relative filename to TEST methods on
*/
private static void test( String filename )
{
if ( DEBUGGING )
{
final File f = new File( rootDir, filename );
out.println( "--------------------------------------------" );
out.println( "Testing file: [" + f + "]" );
out.println( "basicNameWithExtension: ["
+ basicNameWithExtension( f )
+ "]" );
out.println( "coreName: ["
+ EIO.getCoreName( f )
+ "]" );
out.println( "qualifiedCanonicalNameWithBackslashes: ["
+ qualifiedCanonicalNameWithBackslashes( f )
+ "]" );
out.println( "dirWithSlashes: ["
+ dirWithSlashes( f )
+ "]" );
String[] dirs = dirSegments( dirWithSlashes( f ) );
out.println( "segments: [" + dirs.length + "]" );
for ( String dir : dirs )
{
out.println( "seg: [" + dir + "]" );
}
out.println( "extension: [" + EIO.getExtension( f ) + "]" );
out.println( "qualifiedNameWithBackslashes: ["
+ qualifiedNameWithBackslashes( f )
+ "]" );
out.println( "getParent: [" + getParent( f ) + "]" );
out.println( "serverQualifiedURLWithSlashes: ["
+ serverQualifiedURLWithSlashes( f )
+ "]" );
out.println( "uPathName: ["
+ uPathName( f )
+ "]" );
}
} // /methods
/**
* Get name.ext without path. Not Canonical. Will not contain any / or \.
* e.g. regex.html
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return unqualified filename e.g. bushisms.html.
*/
@SuppressWarnings( { "WeakerAccess" } )
public static String basicNameWithExtension( File fileBeingDistributed )
{
return fileBeingDistributed.getName();
} // /methods
/**
* Used to fine tune initial StringBuilder size estimates.
* Insert a call to checkStringBuilderEstimate just before toString.
*
* @param sb the StringBuilder to check.
* @param lowSize the lowest expected size of a result with this StringBuilder.
* @param highSize usually the initial size the StringBuilder was allocated,
* @param fileBeingDistributed the file currently being processed.
*/
@SuppressWarnings( { "ThrowableInstanceNeverThrown" } )
public static void checkStringBuilderEstimate( StringBuilder sb, int lowSize, int highSize,
@Nullable final File fileBeingDistributed )
{
final int size = sb.length();
final String howDifferent;
if ( size > highSize )
{
howDifferent = "longer";
}
else if ( size < lowSize )
{
howDifferent = "shorter";
}
else
{
return;
}
err.println(); // to get on fresh line from letter progress index.
if ( configuration.isUsingJet() )
{
// Jet does not let us look at a stack trace. We thus don't know where the call came from.
// In theory we turn turn better tracing on, but that defeats the point of lean and mean.
err.println( "Warning: StringBuilder length "
+ size
+ " is "
+ howDifferent
+ " than the expected range "
+ lowSize
+ ".."
+ highSize
+ ( fileBeingDistributed == null ? "" : ( " in file " + fileBeingDistributed.toString() ) )
+ ". Run under Oracle JVM to find out where the problem lies." );
}
else
{
// Oracle JVM lets us figure out where the call came from.
StackTraceElement e = new Throwable().getStackTrace()[ 1 ];
err.println( "Warning: "
+ e.getClassName()
+ "."
+ e.getMethodName()
+ "\n "
+ " line:" + e.getLineNumber()
+ " StringBuilder length "
+ size
+ " is "
+ howDifferent
+ " than the expected range "
+ lowSize
+ ".."
+ highSize
+ ( fileBeingDistributed == null ? "" : ( " in file " + fileBeingDistributed.toString() ) )
+ "." );
}
} // /methods
/**
* expandNoRef expandNoRef to <a class="linkclass">description</a>
*
* @param uPathURL webroot relative link, (also works with absolute URL http://
* @param description text description of link. Could be 0 && !configuration.isUsingXHTML() )
{
sb.append( "target=\"" );
sb.append( frame );
sb.append( "\" " );
}
if ( linkCSSClass != null && linkCSSClass.length() > 0 )
{
sb.append( "class=\"" );
sb.append( linkCSSClass );
sb.append( "\" " );
}
sb.append( "href=\"" );
sb.append( Tools.relativeURL( uPathURL, fileBeingDistributed ) );
sb.append( "\">" );
sb.append( description );
sb.append( "" );
return sb.toString();
}
} // /methods
/**
* expandNoRef expandNoRef to <a class="linkclass">description</a>
*
* @param uPathURL webroot relative link, (also works with absolute URL http://
* @param description text description of link. (might be E:\mindprod\jgloss\jgloss.html
* E:\mindprod\jgloss\master --> E:\mindprod\jgloss
* Does not check if either file exists.
*
* @param file master file
*
* @return master file, as a File not a String.
*/
public static File correspondingDistributable( File file )
{
final String fn = EIO.getCanOrAbsPath( file );
assert !fn.endsWith( "\\" ) : "correspondingMaster file ends with \\";
assert !fn.contains( "/" ) : "correspondingMaster file contains /";
if ( fn.endsWith( ".html" ) )
{
final int place = fn.lastIndexOf( "\\master\\" );
if ( place >= 0 )
{
// chop out master but leave \
return new File( fn.substring( 0, place ) + fn.substring( place + "\\master".length() ) );
}
else
{
throw new IllegalArgumentException( "correspondingDistributable: illegal filename " + fn );
}
}
else
{
// it is a directory
return new File( ST.chopTrailingString( fn, "\\master" ) );
}
} // /methods
/**
* Get the corresponding master file for this distributed html file e.g.
* E:\mindprod\jgloss\jgloss.html --> E:\mindprod\jgloss\master\jgloss.html
* E:\mindprod\jgloss --> E:\mindprod\jgloss\master
* Does not check if either file exists.
*
* @param file distributed file
*
* @return master file, as a File not a String.
*/
public static File correspondingMaster( File file )
{
final String fn = EIO.getCanOrAbsPath( file );
assert !fn.endsWith( "\\" ) : "correspondingMaster file ends with \\";
assert !fn.contains( "/" ) : "correspondingMaster file contains /";
if ( fn.endsWith( ".html" ) )
{
final int place = fn.lastIndexOf( '\\' );
if ( place >= 0 )
{
return new File( fn.substring( 0, place ) + "\\master" + fn.substring( place ) );
}
else
{
throw new IllegalArgumentException( "correspondingMaster: illegal filename " + fn );
}
}
else
{
// it is a directory
return new File( fn + "\\master" );
}
} // /methods
/**
* Get just the / dir name, relative to webroot.
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return just directory part of the name without trailing /, with embedded /. e.g. politics, or politics/laser
*/
public static String dirWithSlashes( File fileBeingDistributed )
{
assert fileBeingDistributed != null : "null fileBeingDistributed";
String path = uPathName( fileBeingDistributed );
int place = path.lastIndexOf( '/' );
if ( place < 0 )
{
return "";
}
else
{
return path.substring( 0, place );
}
} // /methods
/**
* Strip quotes, tags, and and entities, to allow content to be used in header fields
*
* @param content to flatten
*
* @return flattened text
*/
public static String flatten( String content )
{
return stripQuotes( DeEntifyStrings.flattenHTML( content, ' ' ) ).trim();
} // /methods
/**
* Currently duplicated in com.mindprod.commandline.CommandLine Smarter version of File.getParent. It has yet to be
* tested with all combinations of absolute, relative, root, ., .., ../ ../sub C: E:\ etc.
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return parent directory that file lives in as File object, ready to use in FilenameFilter.accept.
*/
@SuppressWarnings( { "WeakerAccess" } )
public static File getParent( File fileBeingDistributed )
{
String parent = fileBeingDistributed.getParent();
if ( parent == null || parent.length() == 0 )
{
// Oracle's method failed, try another.
String fullName = EIO.getCanOrAbsPath( fileBeingDistributed );
String name = fileBeingDistributed.getName();
if ( name == null )
{
name = "";
}
parent = fullName.substring( 0, fullName.length() - name.length() );
if ( parent == null || parent.length() == 0 )
{
throw new IllegalArgumentException( "Tools:getParent failed" );
}
}
return new File( parent );
} // /methods
/**
* is this file of the form E:\mindprod\xxx\master\xxx.html
*
* @param file file/dir to test
*
* @return true if file in a master directory, e.g. a master containing comments and macros
*/
public static boolean isFileAMaster( File file )
{
final String fn = EIO.getCanOrAbsPath( file );
return fn.contains( "\\master\\" ) || fn.endsWith( "\\master" );
} // /methods
/**
* TEST harness
*
* @param args not used
*/
@SuppressWarnings( { "UnusedParameters" } )
public static void main( String[] args )
{
if ( DEBUGGING )
{
out.println( isFileAMaster( new File( "E:\\mindprod\\jgloss\\jgloss.html" ) ) ); // debug test
out.println( isFileAMaster( new File( "E:/mindprod/jgloss/jgloss.html" ) ) ); // debug test
out.println( isFileAMaster( new File( "E:\\mindprod\\jgloss\\master\\jgloss.html" ) ) );
out.println( isFileAMaster( new File( "E:/mindprod/jgloss/master/jgloss.html" ) ) );
out.println( correspondingMaster( new File( "E:\\mindprod\\jgloss\\jgloss.html" ) ) );
out.println( correspondingDistributable( new File( "E:\\mindprod\\jgloss\\master\\jgloss.html" ) ) );
out.println( flatten( "Roedy’s" ) );
test( "livinglove/methods/icd.html" );
test( "livinglove/ll.html" );
test( "products.html" );
test( "kjv/2_Thessalonians/1.html" );
linkTest( "livinglove/methods/icd.html",
"livinglove/methods/pws.html" );
linkTest( "livinglove/methods/icd.html", "livinglove/ll.html" );
linkTest( "livinglove/methods/icd.html", "products.html" );
linkTest( "livinglove/methods/pws.html",
"livinglove/methods/icd.html" );
linkTest( "livinglove/ll.html", "livinglove/methods/icd.html" );
linkTest( "products.html", "livinglove/methods/icd.html" );
linkTest( "index.html", "products.html" );
linkTest( "index.html", "livinglove/ll.html" );
linkTest( "livinglove/ll.html", "livinglove/llbio.html" );
linkTest( "livinglove/ll.html", "politics/laser/freetrade.html" );
linkTest( "livinglove/ll.html", "animalrights/dolphin.html" );
}
}
/**
* Get / name relative to webroot of a file in the same directory as the file being processed.
*
* @param nearbyFilename name of file in same directory as fileBeingDistributed
* @param fileBeingDistributed the file having its macros expanded
*
* @return name relative to LOCAL_WEBROOT_WITH_BACKSLASHES with / in names e.g.politics/bushisms.html
*/
public static String nearbyUPathName( String nearbyFilename, File fileBeingDistributed
)
{
return uPathName( new File( fileBeingDistributed.getParent(), nearbyFilename ) );
} // /methods
/**
* convert spaces to
*
* @param s String to convert, might contain tags
*
* @return String with all spaces made non-breaking. Easier with CSS white-space:nowrap
*/
public static String nonBreaking( String s )
{
// leave as StringBuilder
final StringBuilder sb = new StringBuilder( s.length() * 2 );
boolean insideTag = false;
for ( int i = 0, n = s.length(); i < n; i++ )
{
char c = s.charAt( i );
switch ( c )
{
case ' ':
if ( insideTag )
{
sb.append( ' ' );
}
else
{
sb.append( " " );
}
break;
case '<':
insideTag = true;
sb.append( '<' );
break;
case '>':
insideTag = false;
sb.append( '>' );
break;
default:
sb.append( c );
}
}
checkStringBuilderEstimate( sb, s.length(), s.length() * 3, null );
// if did not increase length of string, we did not do anything, so return original.
return sb.length() == s.length() ? s : sb.toString();
}// /methods
/**
* Get fully qualified name locally, path, name and ext.
* e.g E:\mindprod\politics\bushisms.html
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return fully qualified filename with \ in names e.g. E:\mindprod\politics\bushisms.html with caps in filename
* exact.
*/
@SuppressWarnings( { "WeakerAccess" } )
public static String qualifiedCanonicalNameWithBackslashes( File fileBeingDistributed )
{
return EIO.getCanOrAbsPath( fileBeingDistributed );
} // /methods
/**
* Get fully qualified name locally, path, name and ext., not canonical
* E:\mindprod\politics\bushisms.html
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return fully qualified filename with \ in names e.g. E:\mindprod\politics\bushisms.html with caps in filename
* exact.
*/
@SuppressWarnings( { "WeakerAccess" } )
public static String qualifiedNameWithBackslashes( File fileBeingDistributed )
{
assert fileBeingDistributed != null : "null fileBeingDistributed";
return EIO.getCanOrAbsPath( fileBeingDistributed )
.replace( '/', File.separatorChar );
} // /methods
/**
* Generate a relative url from the current file, no <a href= xxx>/a<etc.
*
* @param uPathFilename file where link jumps to, webRoot relative
* @param fileBeingDistributed where the link will be embedded
*
* @return relative url, with slashes.
* @see #completeLink
*/
public static String relativeURL( String uPathFilename, File fileBeingDistributed )
{
assert fileBeingDistributed != null : "null fileBeingDistributed";
String[] sourceSegs =
dirSegments( dirWithSlashes( fileBeingDistributed ) );
final File targetFile = new File( rootDir, uPathFilename );
String[] targetSegs = dirSegments( dirWithSlashes( targetFile ) );
// get base filename.ext#xxxxx
String targetName = basicNameWithExtension( targetFile );
// eliminate high order commonality
int last = Math.min( sourceSegs.length, targetSegs.length );
for ( int c = 0; c < last; c++ )
{
if ( !sourceSegs[ c ].equals( targetSegs[ c ] ) )
{
// we found a difference, note where
last = c;
break;
}
} // end for c
// fell out the bottom. That means source and target directories are
// identical.
// up to but not including last.
// generate URL to work up the tree to common place
final FastCat sb = new FastCat( ( targetSegs.length - last ) * 2 + 2 );
sb.append( getLevelString( sourceSegs.length - last ) );
// work back down toward target
for ( int d = last; d < targetSegs.length; d++ )
{
sb.append( targetSegs[ d ], "/" );
} // end for d
sb.append( targetName );
return sb.toString();
// we do not translate ../index.html to ./ because it screws up if files are loaded from local hard disk.
} // /methods
/**
* Get full url of file on website.
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return fully qualified filename with / in names e.g. http://mindprod.com/politics/bushisms.html with caps in
* filename exact.
*/
@SuppressWarnings( { "WeakerAccess" } )
public static String serverQualifiedURLWithSlashes( File fileBeingDistributed )
{
// chop of the lead E:/mindprod and prepend http://mindprod.com
return "http://" + configuration.getWebsiteDomain() + "/" + uPathName(
fileBeingDistributed );
} // /methods
/**
* Strip and entities, to allow content to be used in header fields
*
* @param content to flatten
*
* @return flattened text
*/
public static String stripEntities( String content )
{
return DeEntifyStrings.deEntifyHTML( content, ' ' ).trim();
}
/**
* Strip tags, and but not other entities, to allow content to be used in header fields
*
* @param content to flatten
*
* @return flattened text
*/
public static String stripHTMLTags( String content )
{
return DeEntifyStrings.stripHTMLTags( content ).trim();
} // /methods
/**
* Strip quotes by converting to ' , and Accented letters to unaccented ones.
* Don't Use entities to avoid bringing in whole package.
*
* @param content to flatten
*
* @return flattened text
*/
@SuppressWarnings( { "WeakerAccess" } )
public static String stripQuotes( String content )
{
final int length = content.length();
// leave as StringBuilder
final StringBuilder sb = new StringBuilder( length + 10 );
for ( int i = 0; i < length; i++ )
{
final char c = content.charAt( i );
switch ( c )
{
case /* Aacute */ '\u00c1':
case /* Acirc */ '\u00c2':
case /* Agrave */ '\u00c0':
case /* Aring */ '\u00c5':
case /* Atilde */ '\u00c3':
case /* Auml */ '\u00c4':
sb.append( 'A' );
break;
case /* AElig */ '\u00c6':
sb.append( "AE" );
break;
case /* Ccedil */ '\u00c7':
sb.append( 'C' );
break;
case /* Eacute */ '\u00c9':
case /* Ecirc */ '\u00ca':
case /* Egrave */ '\u00c8':
case /* Euml */ '\u00cb':
sb.append( 'E' );
break;
case /* Iacute */ '\u00cd':
case /* Icirc */ '\u00ce':
case /* Igrave */ '\u00cc':
case /* Iuml */ '\u00cf':
sb.append( 'I' );
break;
case /* Ntilde */ '\u00d1':
sb.append( 'N' );
break;
case /* OElig */ '\u0152':
sb.append( "OE" );
break;
case /* Oacute */ '\u00d3':
case /* Ocirc */ '\u00d4':
case /* Ograve */ '\u00d2':
case /* Oslash */ '\u00d8':
case /* Otilde */ '\u00d5':
case /* Ouml */ '\u00d6':
sb.append( 'O' );
break;
case /* Uacute */ '\u00da':
case /* Ucirc */ '\u00db':
case /* Ugrave */ '\u00d9':
case /* Upsilon */ '\u03a5':
case /* Uuml */ '\u00dc':
sb.append( 'U' );
break;
case /* Yacute */ '\u00dd':
case /* Yuml */ '\u0178':
sb.append( 'Y' );
break;
case /* aacute */ '\u00e1':
case /* acirc */ '\u00e2':
case /* acute */ '\u00b4':
case /* agrave */ '\u00e0':
case /* aring */ '\u00e5':
case /* atilde */ '\u00e3':
case /* auml */ '\u00e4':
sb.append( 'a' );
break;
case /* aelig */ '\u00e6':
sb.append( "ae" );
break;
case /* ccedil */ '\u00e7':
sb.append( 'c' );
break;
case /* eacute */ '\u00e9':
case /* ecirc */ '\u00ea':
case /* egrave */ '\u00e8':
case /* euml */ '\u00eb':
sb.append( 'e' );
break;
case /* emsp */ '\u2003':
case /* ensp */ '\u2002':
case /* nbsp */ '\u00a0':
sb.append( ' ' );
break;
case /* iacute */ '\u00ed':
case /* icirc */ '\u00ee':
case /* igrave */ '\u00ec':
case /* iuml */ '\u00ef':
sb.append( 'i' );
break;
case /* laquo */ '\u00ab':
case /* ldquo */ '\u201c':
case /* lsaquo */ '\u2039':
case /* lsquo */ '\u2018':
case /* rdquo */ '\u201d':
case /* rsaquo */ '\u203a':
case /* rsquo */ '\u2019':
case /* sbquo */ '\u201a':
case /* quot */ '\u0022':
sb.append( '\'' );
break;
case /* mdash */ '\u2014':
case /* minus */ '\u2212':
case /* ndash */ '\u2013':
sb.append( '-' );
break;
case /* ntilde */ '\u00f1':
sb.append( 'n' );
break;
case /* oacute */ '\u00f3':
case /* ocirc */ '\u00f4':
case /* ograve */ '\u00f2':
case /* oslash */ '\u00f8':
case /* otilde */ '\u00f5':
case /* ouml */ '\u00f6':
sb.append( 'o' );
break;
case /* oelig */ '\u0153':
sb.append( "oe" );
break;
case /* sup1 */ '\u00b9':
sb.append( '1' );
break;
case /* sup2 */ '\u00b2':
sb.append( '2' );
break;
case /* sup3 */ '\u00b3':
sb.append( '3' );
break;
case /* uacute */ '\u00fa':
case /* ucirc */ '\u00fb':
case /* ugrave */ '\u00f9':
case /* uuml */ '\u00fc':
sb.append( 'u' );
break;
case /* yacute */ '\u00fd':
case /* yuml */ '\u00ff':
sb.append( 'y' );
break;
case /* hellip */ '\u2026':
sb.append( "..." );
break;
default:
// we don't tidy up every possible non-ASCII char or strange punctuation
sb.append( c );
}
}
Tools.checkStringBuilderEstimate( sb, length - 10, length + 10, null );
return sb.toString();
} // /methods
/**
* Get local webroot relative name, with backslashes.
*
* @param uPathFileName file you want as File object
*
* @return file as a File
*/
@SuppressWarnings( { "WeakerAccess" } )
public static File toFileFromUPath( String uPathFileName )
{
assert uPathFileName != null : "null uPathFileName";
uPathFileName = ST.trimLeading( uPathFileName, '/' );
return new File( rootDir, uPathFileName );
} // /methods
/**
* Get webroot relative name, with slashes.
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return name relative to LOCAL_WEBROOT_WITH_BACKSLASHES with / in names e.g.politics/bushisms.html
*/
public static String uPathName( File fileBeingDistributed )
{
assert fileBeingDistributed != null : "null fileBeingDistributed";
String fullName = qualifiedNameWithBackslashes( fileBeingDistributed );
assert fullName.toLowerCase()
.startsWith( rootDirNameWithBackSlashes ) :
"not uPath filename ["
+ fullName
+ "]";
// chop off webRoot and lead \
return fullName.substring( rootDirNameWithBackSlashes.length() + 1 ).replace( File.separatorChar, '/' );
} // /methods
/**
* Get webroot relative name, with slashes. webroot file returns "".
*
* @param fileBeingDistributed the file having its macros expanded
*
* @return name relative to LOCAL_WEBROOT_WITH_BACKSLASHES with / in names e.g.politics/bushisms.html
*/
public static String uPathParentWithSlashes( File fileBeingDistributed )
{
if ( fileBeingDistributed == null )
{
throw new IllegalArgumentException( "getCanonicalFile failed: null fileBeingDistributed" );
}
try
{
final String parent = fileBeingDistributed.getCanonicalFile().getParent();
// root dir -> "" not "/".
final int rootLen = rootDirNameWithSlashes.length();
final int parentLen = parent.length();
if ( parentLen == rootLen )
{
return "";
}
else if ( parentLen > rootLen )
{
// chop off webRoot and lead \
return parent.substring( rootDirNameWithSlashes.length() + 1 ).replace( File.separatorChar, '/' );
}
else
{
// parentLen < rootlen
throw new IllegalArgumentException( "Tools.uPathParentWithSlashes failed. file=" + fileBeingDistributed.getAbsolutePath() + "\" parent=" + parent + "\"" );
}
}
catch ( IOException e )
{
throw new IllegalArgumentException( "getCanonicalFile failed" );
}
} // /methods
// /methods
}