created XrefFormatter

This commit is contained in:
Denis Molony 2021-03-04 01:34:09 +10:00
parent a4aa18330c
commit 34c8c16aba
3 changed files with 495 additions and 467 deletions

View File

@ -1,12 +1,7 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
import static com.bytezone.diskbrowser.utilities.Utility.unsignedShort;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.bytezone.diskbrowser.utilities.Utility;
@ -14,40 +9,13 @@ import com.bytezone.diskbrowser.utilities.Utility;
public class ApplesoftBasicProgram extends BasicProgram implements ApplesoftConstants
// -----------------------------------------------------------------------------------//
{
private static final String underline =
"----------------------------------------------------"
+ "----------------------------------------------";
private static final String NEWLINE = "\n";
private final List<SourceLine> sourceLines = new ArrayList<> ();
private final int endPtr;
private final int longestVarName;
private final Map<Integer, List<Integer>> gotoLines = new TreeMap<> ();
private final Map<Integer, List<Integer>> gosubLines = new TreeMap<> ();
private final Map<Integer, List<Integer>> constantsInt = new TreeMap<> ();
private final Map<Float, List<Integer>> constantsFloat = new TreeMap<> ();
private final Map<String, List<Integer>> callLines = new TreeMap<> ();
private final Map<String, List<Integer>> symbolLines = new TreeMap<> ();
private final Map<String, List<Integer>> functionLines = new TreeMap<> ();
private final Map<String, List<Integer>> arrayLines = new TreeMap<> ();
private final Map<String, List<String>> uniqueSymbols = new TreeMap<> ();
private final Map<String, List<String>> uniqueArrays = new TreeMap<> ();
private final List<Integer> stringsLine = new ArrayList<> ();
private final List<String> stringsText = new ArrayList<> ();
private final String formatLeft;
private final String formatLineNumber;
private final String formatRight;
private final int maxDigits;
private UserBasicFormatter userBasicFormatter;
private AppleBasicFormatter appleBasicFormatter;
private DebugBasicFormatter debugBasicFormatter;
private XrefFormatter xrefFormatter;
// ---------------------------------------------------------------------------------//
public ApplesoftBasicProgram (String name, byte[] buffer)
@ -61,24 +29,15 @@ public class ApplesoftBasicProgram extends BasicProgram implements ApplesoftCons
{
SourceLine line = new SourceLine (this, buffer, ptr);
sourceLines.add (line);
checkXref (line);
ptr += line.length; // assumes lines are contiguous
}
endPtr = ptr; // record where the end-of-program marker is
longestVarName = getLongestName ();
maxDigits = getMaxDigits ();
// build format strings based on existing line numbers and variable names
formatLeft = longestVarName > 7 ? "%-" + longestVarName + "." + longestVarName + "s "
: "%-7.7s ";
formatRight = formatLeft.replace ("-", "");
formatLineNumber = "%" + maxDigits + "d ";
userBasicFormatter = new UserBasicFormatter (this, basicPreferences);
appleBasicFormatter = new AppleBasicFormatter (this, basicPreferences);
debugBasicFormatter = new DebugBasicFormatter (this, basicPreferences);
xrefFormatter = new XrefFormatter (this, basicPreferences);
}
// ---------------------------------------------------------------------------------//
@ -109,66 +68,11 @@ public class ApplesoftBasicProgram extends BasicProgram implements ApplesoftCons
appleBasicFormatter.format (text);
if (basicPreferences.showAllXref)
addXref (text);
xrefFormatter.format (text);
return Utility.rtrim (text);
}
// ---------------------------------------------------------------------------------//
private void addXref (StringBuilder fullText)
// ---------------------------------------------------------------------------------//
{
if (basicPreferences.showSymbols)
{
if (!symbolLines.isEmpty ())
showSymbolsLeft (fullText, symbolLines, "Var");
if (!arrayLines.isEmpty ())
showSymbolsLeft (fullText, arrayLines, "Array");
}
if (basicPreferences.showDuplicateSymbols)
{
if (!uniqueSymbols.isEmpty ())
showDuplicates (fullText, uniqueSymbols, "Var");
if (!uniqueArrays.isEmpty ())
showDuplicates (fullText, uniqueArrays, "Array");
}
if (basicPreferences.showFunctions && !functionLines.isEmpty ())
showSymbolsLeft (fullText, functionLines, "Fnction");
if (basicPreferences.showConstants)
{
if (!constantsInt.isEmpty ())
showSymbolsRightInt (fullText, constantsInt, "Integer");
if (!constantsFloat.isEmpty ())
showSymbolsRightFloat (fullText, constantsFloat, "Float");
if (stringsLine.size () > 0)
{
heading (fullText, formatRight, "Line", "String");
for (int i = 0; i < stringsLine.size (); i++)
fullText.append (String.format (formatRight + "%s%n", stringsLine.get (i),
stringsText.get (i)));
}
}
if (basicPreferences.showXref)
{
if (!gosubLines.isEmpty ())
showSymbolsRight (fullText, gosubLines, "GOSUB");
if (!gotoLines.isEmpty ())
showSymbolsRight (fullText, gotoLines, "GOTO");
}
if (basicPreferences.showCalls && !callLines.isEmpty ())
showSymbolsLeftRight (fullText, callLines, " CALL");
}
// ---------------------------------------------------------------------------------//
List<SourceLine> getSourceLines ()
// ---------------------------------------------------------------------------------//
@ -190,378 +94,13 @@ public class ApplesoftBasicProgram extends BasicProgram implements ApplesoftCons
return endPtr;
}
// ---------------------------------------------------------------------------------//
private int getMaxDigits ()
// ---------------------------------------------------------------------------------//
{
if (sourceLines.size () == 0)
return 4; // anything non-zero
SourceLine lastLine = sourceLines.get (sourceLines.size () - 1);
return (lastLine.lineNumber + "").length ();
}
// ---------------------------------------------------------------------------------//
private int getLongestName ()
// ---------------------------------------------------------------------------------//
{
int longestName = getLongestName (symbolLines, 0);
longestName = getLongestName (arrayLines, longestName);
longestName = getLongestName (functionLines, longestName);
return longestName;
}
// ---------------------------------------------------------------------------------//
private void heading (StringBuilder fullText, String format, String... heading)
// ---------------------------------------------------------------------------------//
{
if (fullText.charAt (fullText.length () - 2) != '\n')
fullText.append (NEWLINE);
fullText.append (String.format (format, underline));
fullText.append (underline);
fullText.append (NEWLINE);
fullText.append (String.format (format, heading[0]));
if (heading.length == 1)
fullText.append ("Line numbers");
else
fullText.append (heading[1]);
fullText.append (NEWLINE);
fullText.append (String.format (format, underline));
fullText.append (underline);
fullText.append (NEWLINE);
}
// ---------------------------------------------------------------------------------//
private void showDuplicates (StringBuilder fullText, Map<String, List<String>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
boolean headingShown = false;
for (String key : map.keySet ())
{
List<String> usage = map.get (key);
if (usage.size () > 1)
{
if (!headingShown)
{
headingShown = true;
heading (fullText, formatLeft, heading, "Duplicate Names");
}
String line = usage.toString ();
line = line.substring (1, line.length () - 1);
fullText.append (String.format ("%-6s %s%n", key, line));
}
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsLeft (StringBuilder fullText, Map<String, List<Integer>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatLeft, heading);
for (String symbol : map.keySet ()) // left-justify strings
{
if (symbol.length () <= 7)
appendLineNumbers (fullText, String.format (formatLeft, symbol),
map.get (symbol));
else
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsLeftRight (StringBuilder fullText,
Map<String, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatLeft, heading);
for (String symbol : map.keySet ()) // left-justify strings
{
if (isNumeric (symbol))
appendLineNumbers (fullText, String.format (formatRight, symbol),
map.get (symbol));
else if (symbol.length () <= 7)
appendLineNumbers (fullText, String.format (formatLeft, symbol),
map.get (symbol));
else
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
}
}
// ---------------------------------------------------------------------------------//
private boolean isNumeric (String value)
// ---------------------------------------------------------------------------------//
{
byte[] bytes = value.getBytes ();
int start = value.charAt (0) == Utility.ASCII_MINUS ? 1 : 0;
for (int i = start; i < bytes.length; i++)
if (!isPossibleNumber (bytes[i]))
return false;
return true;
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRight (StringBuilder fullText, Map<Integer, List<Integer>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (Integer symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRightInt (StringBuilder fullText,
Map<Integer, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (int symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRightFloat (StringBuilder fullText,
Map<Float, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (float symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void appendLineNumbers (StringBuilder fullText, String symbol,
List<Integer> lineNumbers)
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (symbol);
for (int lineNo : lineNumbers)
{
if (text.length () > underline.length () - maxDigits + longestVarName)
{
fullText.append (text);
fullText.append (NEWLINE);
text.setLength (0);
text.append (String.format (formatRight, ""));
}
text.append (String.format (formatLineNumber, lineNo));
}
if (text.length () > longestVarName + 3)
fullText.append (text + "\n");
}
// ---------------------------------------------------------------------------------//
private int getLongestName (Map<String, List<Integer>> map, int longestName)
// ---------------------------------------------------------------------------------//
{
for (String symbol : map.keySet ())
if (symbol.length () > longestName)
longestName = symbol.length ();
return longestName;
}
// ---------------------------------------------------------------------------------//
private void addHeader (StringBuilder pgm)
// ---------------------------------------------------------------------------------//
{
pgm.append ("Name : " + name + "\n");
pgm.append (String.format ("Length : $%04X (%<,d)%n", buffer.length));
pgm.append (String.format ("Load at : $%04X (%<,d)%n%n", getLoadAddress ()));
}
// ---------------------------------------------------------------------------------//
int getLoadAddress ()
// ---------------------------------------------------------------------------------//
{
return (buffer.length > 1) ? unsignedShort (buffer, 0) - getLineLength (0) : 0;
}
// ---------------------------------------------------------------------------------//
private int getLineLength (int ptr)
// ---------------------------------------------------------------------------------//
{
int linkField = unsignedShort (buffer, ptr);
if (linkField == 0)
return 2;
ptr += 4; // skip link field and line number
int length = 5;
while (ptr < buffer.length && buffer[ptr++] != 0)
length++;
assert length == ptr;
return length;
}
// ---------------------------------------------------------------------------------//
private void checkXref (SourceLine line)
// ---------------------------------------------------------------------------------//
{
for (SubLine subline : line.sublines)
{
for (String symbol : subline.getVariables ())
checkVar (symbol, line.lineNumber, symbolLines, uniqueSymbols);
for (String symbol : subline.getArrays ())
checkVar (symbol, line.lineNumber, arrayLines, uniqueArrays);
for (String symbol : subline.getFunctions ())
checkFunction (line.lineNumber, symbol);
for (int targetLine : subline.getGosubLines ())
addNumberInt (line.lineNumber, targetLine, gosubLines);
for (int targetLine : subline.getGotoLines ())
addNumberInt (line.lineNumber, targetLine, gotoLines);
for (int num : subline.getConstantsInt ())
addNumberInt (line.lineNumber, num, constantsInt);
for (float num : subline.getConstantsFloat ())
addNumberFloat (line.lineNumber, num, constantsFloat);
if (subline.callTarget != null)
addString (line.lineNumber, subline.callTarget, callLines);
for (String s : subline.getStringsText ())
{
stringsText.add (s);
stringsLine.add (line.lineNumber);
}
}
}
// ---------------------------------------------------------------------------------//
private void checkVar (String var, int lineNumber, Map<String, List<Integer>> map,
Map<String, List<String>> unique)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (var);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (var, lines);
}
if (lines.size () == 0)
lines.add (lineNumber);
else
{
int lastLine = lines.get (lines.size () - 1);
if (lastLine != lineNumber)
lines.add (lineNumber);
}
checkUniqueName (var, unique);
}
// ---------------------------------------------------------------------------------//
private void checkFunction (int sourceLine, String var)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = functionLines.get (var);
if (lines == null)
{
lines = new ArrayList<> ();
functionLines.put (var, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addNumberInt (int sourceLine, Integer key, Map<Integer, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addNumberFloat (int sourceLine, Float key, Map<Float, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addString (int sourceLine, String key, Map<String, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addLine (List<Integer> lines, int lineNumber)
// ---------------------------------------------------------------------------------//
{
if (lines.size () == 0)
lines.add (lineNumber);
else
{
int lastLine = lines.get (lines.size () - 1);
if (lastLine != lineNumber)
lines.add (lineNumber);
}
}
// ---------------------------------------------------------------------------------//
private void checkUniqueName (String symbol, Map<String, List<String>> map)
// ---------------------------------------------------------------------------------//
{
String uniqueName = getUniqueName (symbol);
List<String> usage = map.get (uniqueName);
if (usage == null)
{
usage = new ArrayList<> ();
map.put (uniqueName, usage);
}
if (!usage.contains (symbol))
usage.add (symbol);
}
// ---------------------------------------------------------------------------------//
private String getUniqueName (String symbolName)
// ---------------------------------------------------------------------------------//
{
int ptr = symbolName.length () - 1;
if (symbolName.charAt (ptr) == Utility.ASCII_DOLLAR // string
|| symbolName.charAt (ptr) == Utility.ASCII_PERCENT) // integer
ptr--;
return (ptr <= 1) ? symbolName
: symbolName.substring (0, 2) + symbolName.substring (ptr + 1);
pgm.append (String.format ("Load at : $%04X (%<,d)%n%n",
BasicFormatter.getLoadAddress (buffer)));
}
}

View File

@ -1,5 +1,7 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.unsignedShort;
import java.util.List;
import com.bytezone.diskbrowser.gui.BasicPreferences;
@ -35,6 +37,33 @@ public abstract class BasicFormatter implements ApplesoftConstants
int getLoadAddress ()
// ---------------------------------------------------------------------------------//
{
return program.getLoadAddress ();
// return program.getLoadAddress ();
return getLoadAddress (buffer);
}
// ---------------------------------------------------------------------------------//
static int getLoadAddress (byte[] buffer)
// ---------------------------------------------------------------------------------//
{
return (buffer.length > 1) ? unsignedShort (buffer, 0) - getLineLength (buffer, 0)
: 0;
}
// ---------------------------------------------------------------------------------//
private static int getLineLength (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
int linkField = unsignedShort (buffer, ptr);
if (linkField == 0)
return 2;
ptr += 4; // skip link field and line number
int length = 5;
while (ptr < buffer.length && buffer[ptr++] != 0)
length++;
assert length == ptr;
return length;
}
}

View File

@ -0,0 +1,460 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.bytezone.diskbrowser.gui.BasicPreferences;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class XrefFormatter extends BasicFormatter
// -----------------------------------------------------------------------------------//
{
private static final String underline =
"----------------------------------------------------"
+ "----------------------------------------------";
private final Map<Integer, List<Integer>> gotoLines = new TreeMap<> ();
private final Map<Integer, List<Integer>> gosubLines = new TreeMap<> ();
private final Map<Integer, List<Integer>> constantsInt = new TreeMap<> ();
private final Map<Float, List<Integer>> constantsFloat = new TreeMap<> ();
private final Map<String, List<Integer>> callLines = new TreeMap<> ();
private final Map<String, List<Integer>> symbolLines = new TreeMap<> ();
private final Map<String, List<Integer>> functionLines = new TreeMap<> ();
private final Map<String, List<Integer>> arrayLines = new TreeMap<> ();
private final Map<String, List<String>> uniqueSymbols = new TreeMap<> ();
private final Map<String, List<String>> uniqueArrays = new TreeMap<> ();
private final List<Integer> stringsLine = new ArrayList<> ();
private final List<String> stringsText = new ArrayList<> ();
private final String formatLeft;
private final String formatLineNumber;
private final String formatRight;
private final int longestVarName;
private final int maxDigits;
// ---------------------------------------------------------------------------------//
public XrefFormatter (ApplesoftBasicProgram program, BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
super (program, basicPreferences);
for (SourceLine sourceLine : program.getSourceLines ())
checkXref (sourceLine);
longestVarName = getLongestName ();
maxDigits = getMaxDigits ();
// build format strings based on existing line numbers and variable names
formatLeft = longestVarName > 7 ? "%-" + longestVarName + "." + longestVarName + "s "
: "%-7.7s ";
formatRight = formatLeft.replace ("-", "");
formatLineNumber = "%" + maxDigits + "d ";
}
// ---------------------------------------------------------------------------------//
void checkXref (SourceLine line)
// ---------------------------------------------------------------------------------//
{
for (SubLine subline : line.sublines)
{
for (String symbol : subline.getVariables ())
checkVar (symbol, line.lineNumber, symbolLines, uniqueSymbols);
for (String symbol : subline.getArrays ())
checkVar (symbol, line.lineNumber, arrayLines, uniqueArrays);
for (String symbol : subline.getFunctions ())
checkFunction (line.lineNumber, symbol);
for (int targetLine : subline.getGosubLines ())
addNumberInt (line.lineNumber, targetLine, gosubLines);
for (int targetLine : subline.getGotoLines ())
addNumberInt (line.lineNumber, targetLine, gotoLines);
for (int num : subline.getConstantsInt ())
addNumberInt (line.lineNumber, num, constantsInt);
for (float num : subline.getConstantsFloat ())
addNumberFloat (line.lineNumber, num, constantsFloat);
if (subline.callTarget != null)
addString (line.lineNumber, subline.callTarget, callLines);
for (String s : subline.getStringsText ())
{
stringsText.add (s);
stringsLine.add (line.lineNumber);
}
}
}
// ---------------------------------------------------------------------------------//
@Override
public void format (StringBuilder fullText)
// ---------------------------------------------------------------------------------//
{
if (basicPreferences.showSymbols)
{
if (!symbolLines.isEmpty ())
showSymbolsLeft (fullText, symbolLines, "Var");
if (!arrayLines.isEmpty ())
showSymbolsLeft (fullText, arrayLines, "Array");
}
if (basicPreferences.showDuplicateSymbols)
{
if (!uniqueSymbols.isEmpty ())
showDuplicates (fullText, uniqueSymbols, "Var");
if (!uniqueArrays.isEmpty ())
showDuplicates (fullText, uniqueArrays, "Array");
}
if (basicPreferences.showFunctions && !functionLines.isEmpty ())
showSymbolsLeft (fullText, functionLines, "Fnction");
if (basicPreferences.showConstants)
{
if (!constantsInt.isEmpty ())
showSymbolsRightInt (fullText, constantsInt, "Integer");
if (!constantsFloat.isEmpty ())
showSymbolsRightFloat (fullText, constantsFloat, "Float");
if (stringsLine.size () > 0)
{
heading (fullText, formatRight, "Line", "String");
for (int i = 0; i < stringsLine.size (); i++)
fullText.append (String.format (formatRight + "%s%n", stringsLine.get (i),
stringsText.get (i)));
}
}
if (basicPreferences.showXref)
{
if (!gosubLines.isEmpty ())
showSymbolsRight (fullText, gosubLines, "GOSUB");
if (!gotoLines.isEmpty ())
showSymbolsRight (fullText, gotoLines, "GOTO");
}
if (basicPreferences.showCalls && !callLines.isEmpty ())
showSymbolsLeftRight (fullText, callLines, " CALL");
}
// ---------------------------------------------------------------------------------//
private int getMaxDigits ()
// ---------------------------------------------------------------------------------//
{
if (sourceLines.size () == 0)
return 4; // anything non-zero
SourceLine lastLine = sourceLines.get (sourceLines.size () - 1);
return (lastLine.lineNumber + "").length ();
}
// ---------------------------------------------------------------------------------//
private int getLongestName ()
// ---------------------------------------------------------------------------------//
{
int longestName = getLongestName (symbolLines, 0);
longestName = getLongestName (arrayLines, longestName);
longestName = getLongestName (functionLines, longestName);
return longestName;
}
// ---------------------------------------------------------------------------------//
private int getLongestName (Map<String, List<Integer>> map, int longestName)
// ---------------------------------------------------------------------------------//
{
for (String symbol : map.keySet ())
if (symbol.length () > longestName)
longestName = symbol.length ();
return longestName;
}
// ---------------------------------------------------------------------------------//
private void heading (StringBuilder fullText, String format, String... heading)
// ---------------------------------------------------------------------------------//
{
if (fullText.charAt (fullText.length () - 2) != '\n')
fullText.append (NEWLINE);
fullText.append (String.format (format, underline));
fullText.append (underline);
fullText.append (NEWLINE);
fullText.append (String.format (format, heading[0]));
if (heading.length == 1)
fullText.append ("Line numbers");
else
fullText.append (heading[1]);
fullText.append (NEWLINE);
fullText.append (String.format (format, underline));
fullText.append (underline);
fullText.append (NEWLINE);
}
// ---------------------------------------------------------------------------------//
private void showDuplicates (StringBuilder fullText, Map<String, List<String>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
boolean headingShown = false;
for (String key : map.keySet ())
{
List<String> usage = map.get (key);
if (usage.size () > 1)
{
if (!headingShown)
{
headingShown = true;
heading (fullText, formatLeft, heading, "Duplicate Names");
}
String line = usage.toString ();
line = line.substring (1, line.length () - 1);
fullText.append (String.format ("%-6s %s%n", key, line));
}
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsLeft (StringBuilder fullText, Map<String, List<Integer>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatLeft, heading);
for (String symbol : map.keySet ()) // left-justify strings
{
if (symbol.length () <= 7)
appendLineNumbers (fullText, String.format (formatLeft, symbol),
map.get (symbol));
else
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsLeftRight (StringBuilder fullText,
Map<String, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatLeft, heading);
for (String symbol : map.keySet ()) // left-justify strings
{
if (isNumeric (symbol))
appendLineNumbers (fullText, String.format (formatRight, symbol),
map.get (symbol));
else if (symbol.length () <= 7)
appendLineNumbers (fullText, String.format (formatLeft, symbol),
map.get (symbol));
else
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRight (StringBuilder fullText, Map<Integer, List<Integer>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (Integer symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRightInt (StringBuilder fullText,
Map<Integer, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (int symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRightFloat (StringBuilder fullText,
Map<Float, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (float symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private boolean isNumeric (String value)
// ---------------------------------------------------------------------------------//
{
byte[] bytes = value.getBytes ();
int start = value.charAt (0) == Utility.ASCII_MINUS ? 1 : 0;
for (int i = start; i < bytes.length; i++)
if (!isPossibleNumber (bytes[i]))
return false;
return true;
}
// ---------------------------------------------------------------------------------//
private void appendLineNumbers (StringBuilder fullText, String symbol,
List<Integer> lineNumbers)
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (symbol);
for (int lineNo : lineNumbers)
{
if (text.length () > underline.length () - maxDigits + longestVarName)
{
fullText.append (text);
fullText.append (NEWLINE);
text.setLength (0);
text.append (String.format (formatRight, ""));
}
text.append (String.format (formatLineNumber, lineNo));
}
if (text.length () > longestVarName + 3)
fullText.append (text + "\n");
}
// ---------------------------------------------------------------------------------//
private void checkVar (String var, int lineNumber, Map<String, List<Integer>> map,
Map<String, List<String>> unique)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (var);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (var, lines);
}
if (lines.size () == 0)
lines.add (lineNumber);
else
{
int lastLine = lines.get (lines.size () - 1);
if (lastLine != lineNumber)
lines.add (lineNumber);
}
checkUniqueName (var, unique);
}
// ---------------------------------------------------------------------------------//
private void checkFunction (int sourceLine, String var)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = functionLines.get (var);
if (lines == null)
{
lines = new ArrayList<> ();
functionLines.put (var, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addNumberInt (int sourceLine, Integer key, Map<Integer, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addNumberFloat (int sourceLine, Float key, Map<Float, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addString (int sourceLine, String key, Map<String, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addLine (List<Integer> lines, int lineNumber)
// ---------------------------------------------------------------------------------//
{
if (lines.size () == 0)
lines.add (lineNumber);
else
{
int lastLine = lines.get (lines.size () - 1);
if (lastLine != lineNumber)
lines.add (lineNumber);
}
}
// ---------------------------------------------------------------------------------//
private void checkUniqueName (String symbol, Map<String, List<String>> map)
// ---------------------------------------------------------------------------------//
{
String uniqueName = getUniqueName (symbol);
List<String> usage = map.get (uniqueName);
if (usage == null)
{
usage = new ArrayList<> ();
map.put (uniqueName, usage);
}
if (!usage.contains (symbol))
usage.add (symbol);
}
// ---------------------------------------------------------------------------------//
private String getUniqueName (String symbolName)
// ---------------------------------------------------------------------------------//
{
int ptr = symbolName.length () - 1;
if (symbolName.charAt (ptr) == Utility.ASCII_DOLLAR // string
|| symbolName.charAt (ptr) == Utility.ASCII_PERCENT) // integer
ptr--;
return (ptr <= 1) ? symbolName
: symbolName.substring (0, 2) + symbolName.substring (ptr + 1);
}
}