184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
##
|
|
# Look for non-standard getString and getMessage key arguments.
|
|
# These will normally be variables.
|
|
# Other exceptions:
|
|
# If a key contains special characters, it will be flagged as a variable.
|
|
# The valid characters are a-z, A-Z, 0-9 and underscore.
|
|
#
|
|
# The JMRI source package is required.
|
|
#
|
|
# Dave Sand copyright 2017
|
|
##
|
|
|
|
import io
|
|
import re
|
|
import os
|
|
from os.path import join
|
|
import glob
|
|
|
|
import jmri
|
|
import java
|
|
import org.apache.commons.csv
|
|
from javax.swing import JFileChooser, JOptionPane
|
|
from javax.swing.filechooser import FileNameExtensionFilter
|
|
|
|
dialogTitle = "Class Keys Report"
|
|
print " {}".format(dialogTitle)
|
|
keyList = []
|
|
|
|
# Select a Java program or package directory to be analyzed.
|
|
fc = JFileChooser(FileUtil.getProgramPath())
|
|
fc.setDialogTitle(dialogTitle)
|
|
fc.setFileFilter(FileNameExtensionFilter("Java Program", ["java"]));
|
|
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES)
|
|
ret = fc.showOpenDialog(None)
|
|
if ret == JFileChooser.APPROVE_OPTION:
|
|
selectedItem = fc.getSelectedFile().toString()
|
|
else:
|
|
print 'No file selected, bye'
|
|
quit()
|
|
|
|
# RegEx patterns. Capture the first word after the start of the match string.
|
|
# The first one looks for a word within double quotes.
|
|
# The second one looks for a plain word.
|
|
# A word contains a-z, A-Z, 0-9 or underscore characters.
|
|
reStrKey = re.compile('\W*"(\w+)"\W*[,)]')
|
|
reVarKey = re.compile('\W*(\w+)\W')
|
|
|
|
##
|
|
# Determine class for a variable key.
|
|
# The first getMessage parameter can be a Locale object.
|
|
# variableKey : The current key
|
|
# filePath : The current file being processed
|
|
# return : The class type: String, Locale or Unknown
|
|
##
|
|
def findVariableClass(variableKey, filePath):
|
|
with io.open(filePath, "r", encoding="utf-8") as chkFile:
|
|
for chkLine in chkFile:
|
|
for classType in ['String', 'Locale']:
|
|
chkClassArg = '.*{}\s+{}'.format(classType, variableKey)
|
|
reChkClass = re.compile(chkClassArg)
|
|
chkMatch = re.match(chkClassArg, chkLine)
|
|
if chkMatch is not None:
|
|
return classType
|
|
return 'Unknown'
|
|
|
|
##
|
|
# Search for the next word in the line fragment.
|
|
# lineFragment : The line fragment to be searched.
|
|
# return : A tuple that has the key and key type
|
|
##
|
|
def findKey(lineFragment):
|
|
returnValues = 'None', 'Unknown'
|
|
chkStr = re.match(reStrKey, lineFragment)
|
|
if chkStr is not None:
|
|
returnValues = chkStr.group(1), 'String'
|
|
else:
|
|
chkVar = re.match(reVarKey, lineFragment)
|
|
if chkVar is not None:
|
|
returnValues = chkVar.group(1), 'Variable'
|
|
return returnValues
|
|
|
|
##
|
|
# Search a line of code for occruances of getString or getMessage.
|
|
# Display a message for non-standard key references.
|
|
# All keys are added to a list that can be exported as a CSV file.
|
|
# classLine : The current line of code
|
|
# num : The current line number
|
|
# filePath : The current file path
|
|
##
|
|
def findKeys(classLine, num, filePath):
|
|
for searchText in ['Bundle.getMessage(', 'getString(']:
|
|
searchType = 'Local' if searchText == 'getString(' else 'Bundle'
|
|
searchSize = len(searchText)
|
|
idx = 0
|
|
while idx >= 0 and idx < len(classLine):
|
|
idx = classLine.find(searchText, idx)
|
|
if idx == -1:
|
|
break
|
|
idx += searchSize
|
|
key, keyType = findKey(classLine[idx:])
|
|
if keyType == 'Variable':
|
|
if findVariableClass(key, filePath) == 'Locale':
|
|
key, keyType = findKey(classLine[idx + len(key):])
|
|
if keyType != 'String':
|
|
print " {}, Search Type = {}, Key Type = {}, Key = '{}', \tText = {}".format(num, searchType, keyType, key, classLine.strip())
|
|
keyList.append([num, searchType, keyType, key, classLine.strip()])
|
|
|
|
##
|
|
# Read each line of the supplied file and call the parser
|
|
# filePath : The full path for the file
|
|
##
|
|
def doFile(filePath):
|
|
keyList.append([filePath])
|
|
with io.open(filePath, "r", encoding="utf-8") as classFile:
|
|
num = 0
|
|
for classLine in classFile:
|
|
num += 1
|
|
findKeys(classLine, num, filePath)
|
|
|
|
# Process a single file selection
|
|
if os.path.isfile(selectedItem):
|
|
print '\n Check {}'.format(selectedItem)
|
|
doFile(selectedItem)
|
|
|
|
# Process package directory selection
|
|
if os.path.isdir(selectedItem):
|
|
globArgument = join(selectedItem, "*java")
|
|
for className in glob.glob(globArgument):
|
|
if os.path.basename(className) == 'Bundle.java':
|
|
continue
|
|
print '\n Check {}'.format(className)
|
|
doFile(className)
|
|
##
|
|
# Create the CSV header record
|
|
# csvFile : The output file
|
|
##
|
|
def writeHeader(csvFile):
|
|
csvFile.print("Line #")
|
|
csvFile.print("Search Type")
|
|
csvFile.print("Key Type")
|
|
csvFile.print("Key")
|
|
csvFile.print("Line Text / Class File")
|
|
csvFile.println()
|
|
|
|
##
|
|
# Create the CSV detail record
|
|
# The file path and name go into column 5
|
|
# csvFile : The output file
|
|
##
|
|
def writeDetails(csvFile):
|
|
for keyRow in keyList:
|
|
if len(keyRow) == 1:
|
|
csvFile.print("")
|
|
csvFile.print("")
|
|
csvFile.print("")
|
|
csvFile.print("")
|
|
csvFile.print(keyRow[0])
|
|
else:
|
|
csvFile.print('{}'.format(keyRow[0]))
|
|
csvFile.print(keyRow[1])
|
|
csvFile.print(keyRow[2])
|
|
csvFile.print(keyRow[3])
|
|
csvFile.print(keyRow[4])
|
|
csvFile.println()
|
|
|
|
# Provide the option to export the full class key list to an external file.
|
|
saveResp = JOptionPane.showConfirmDialog(None, "Do you want to export the full class key list to a CSV file?", dialogTitle, JOptionPane.YES_NO_OPTION)
|
|
if saveResp == 0:
|
|
fo = JFileChooser(FileUtil.getUserFilesPath())
|
|
fo.setDialogTitle(dialogTitle)
|
|
fo.setFileFilter(FileNameExtensionFilter("CSV", ["csv"]));
|
|
ret = fo.showSaveDialog(None)
|
|
if ret == JFileChooser.APPROVE_OPTION:
|
|
keyFile = fo.getSelectedFile().toString()
|
|
csvFile = org.apache.commons.CSVPrinter(java.io.BufferedWriter(java.io.FileWriter(keyFile)),java.nio.charset.Charset.defaultCharset(),org.apache.commons.csv.CSVFormat.DEFAULT)
|
|
writeHeader(csvFile)
|
|
writeDetails(csvFile)
|
|
csvFile.flush()
|
|
csvFile.close()
|
|
JOptionPane.showMessageDialog(None,"Class keys export completed",dialogTitle,JOptionPane.INFORMATION_MESSAGE)
|
|
|
|
print '\n {} Done'.format(dialogTitle)
|
|
|