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> gotoLines = new TreeMap<> (); private final Map> gosubLines = new TreeMap<> (); private final Map> constantsInt = new TreeMap<> (); private final Map> constantsFloat = new TreeMap<> (); private final Map> callLines = new TreeMap<> (); private final Map> symbolLines = new TreeMap<> (); private final Map> functionLines = new TreeMap<> (); private final Map> arrayLines = new TreeMap<> (); private final Map> uniqueSymbols = new TreeMap<> (); private final Map> uniqueArrays = new TreeMap<> (); private final List stringsLine = new ArrayList<> (); private final List 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 append (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.showGosubGoto) { 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> 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> map, String heading) // ---------------------------------------------------------------------------------// { boolean headingShown = false; for (String key : map.keySet ()) { List 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> 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> 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> 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> 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> 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 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> map, Map> unique) // ---------------------------------------------------------------------------------// { List 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 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> map) // ---------------------------------------------------------------------------------// { List 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> map) // ---------------------------------------------------------------------------------// { List 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> map) // ---------------------------------------------------------------------------------// { List lines = map.get (key); if (lines == null) { lines = new ArrayList<> (); map.put (key, lines); } addLine (lines, sourceLine); } // ---------------------------------------------------------------------------------// private void addLine (List 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> map) // ---------------------------------------------------------------------------------// { String uniqueName = getUniqueName (symbol); List 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); } }