Created
January 10, 2018 23:37
-
-
Save adriannier/a778fc4afb71f16d0ad90297601a815e to your computer and use it in GitHub Desktop.
Processes FileMaker scripts or script steps on the clipboard and displays how many times each variable is used.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* | |
AppleScript: Count FileMaker Variables | |
Processes FileMaker scripts or script steps on the clipboard | |
and displays how many times each variable is used | |
----------------------------------------------------------------------------------- | |
Copyright (c) 2018 by Adrian Nier (http://adriannier.de) | |
Permission is hereby granted, free of charge, to any person obtaining a copy of | |
this software and associated documentation files (the "Software"), to deal in the | |
Software without restriction, including without limitation the rights to use, copy, | |
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, | |
and to permit persons to whom the Software is furnished to do so, subject to the | |
following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
----------------------------------------------------------------------------------- | |
Usage: | |
1. Select one or many FileMaker scripts or script steps | |
2. Run this script | |
Result: | |
A dialog appears showing how many times any found variable is used in both *Set Variable* steps and as part of calculations. | |
----------------------------------------------------------------------------------- | |
*) | |
on run | |
displayVariableReport() | |
end run | |
on ______________________________________________________REPORTS() | |
end ______________________________________________________REPORTS | |
on displayVariableReport() | |
(* | |
Displays a dialog with information about the variables found in the currently copied FileMaker script(s) or script step(s). | |
*) | |
-- Get script steps from clipboard | |
set fmXML to fmDataTypesFromClipboardAsXML({"FMScriptSteps", "FMScripts"}) | |
if fmXML is "" then return | |
-- Protect global variables | |
set fmXML to snr(fmXML, "$$", "<GLOBALVAR>") | |
-- Get local variable names | |
set allVariableNames to wordsStartingWith(fmXML, "$") | |
set uniqueVariableNames to uniqueWordsStartingWith(fmXML, "$") | |
-- Restore global variables | |
set fmXML to snr(fmXML, "<GLOBALVAR>", "$$") | |
-- Get global variable names | |
set allVariableNames to allVariableNames & wordsStartingWith(fmXML, "$$") | |
set uniqueVariableNames to uniqueWordsStartingWith(fmXML, "$$") & uniqueVariableNames | |
-- Initialize loop variables | |
set varInfos to {} | |
set singleUseVariableFound to false | |
repeat with varName in uniqueVariableNames | |
set varUseCount to countMatchesInList(varName as text, allVariableNames) | |
if varUseCount = 1 then | |
-- Found variable used only once | |
set singleUseVariableFound to true | |
end if | |
if (varUseCount < 10) then | |
set varUseCount to " " & (varUseCount as text) | |
else if (varUseCount < 100) then | |
set varUseCount to " " & (varUseCount as text) | |
else if (varUseCount < 1000) then | |
set varUseCount to " " & (varUseCount as text) | |
end if | |
set end of varInfos to (varUseCount as text) & "× " & (varName as text) | |
end repeat | |
-- Sort variable information | |
set varInfos to sortList(varInfos) | |
-- Convert variable information to text | |
set varInfo to listToText(varInfos, ASCII character 10) | |
-- Display dialog | |
activate | |
if uniqueVariableNames is {} then | |
display alert "No variables found" message "The copied scripts or script steps use no variables." as critical | |
else if singleUseVariableFound then | |
display alert "Found variables only used once" message varInfo as critical | |
else | |
display alert "All variables are used at least twice" message varInfo | |
end if | |
end displayVariableReport | |
on ________________________________________OUTPUT_IN_TEXTEDIT() | |
end ________________________________________OUTPUT_IN_TEXTEDIT | |
on outputClipboardInTextEdit() | |
(* | |
Outputs the current clipboard in TextEdit. | |
*) | |
try | |
set xmlSource to fmDataTypesFromClipboardAsFormattedXML(false) | |
outputXMLInTextEdit(xmlSource, "FMClipboard") | |
on error eMsg number eNum | |
error "outputClipboardInTextEdit: " & eMsg number eNum | |
end try | |
end outputClipboardInTextEdit | |
on outputXMLInTextEdit(xmlSource, outputName) | |
(* | |
Outputs the XML in TextEdit using a temporary file with the specified name. | |
*) | |
try | |
-- Get FileMaker data type | |
set fmDataType to fmDataTypeOfXML(xmlSource) | |
if fmDataType is "FMInvalidDataType" then return | |
-- Make sure a valid output name is set | |
if outputName is false then | |
set outputName to fmDataType | |
end if | |
-- Add suffix to document name if necessary | |
if outputName does not end with ".xml" then | |
set documentName to outputName & ".xml" | |
else | |
set documentName to outputName | |
end if | |
-- Is the document open in TextEdit? | |
tell application "TextEdit" | |
set documentExists to (exists document documentName) | |
end tell | |
if documentExists then | |
-- Close document if necessary | |
tell application "TextEdit" to close document documentName | |
end if | |
-- Generate file path | |
set filePath to temporaryPathWithNameAndSuffix(outputName, ".xml") | |
-- Write XML to file | |
writeToFileAtPath(xmlSource, filePath) | |
-- Open file with TextEdit | |
do shell script "open -a /Applications/TextEdit.app " & qpp(filePath) | |
on error eMsg number eNum | |
error "outputXMLInTextEdit: " & eMsg number eNum | |
end try | |
end outputXMLInTextEdit | |
on __________________________________CLIPBOARD_MANIPULATION() | |
end __________________________________CLIPBOARD_MANIPULATION | |
on xmlFileToClipboard(filePath) | |
(* | |
Puts the contents of the specified XML file to the clipboard for usage in FileMaker. | |
*) | |
try | |
-- Convert file path to HFS path | |
set filePath to anyPathToHFSPath(filePath) | |
-- Get the XML from the file | |
set xmlSource to readFile(filePath) | |
-- Clean up XML | |
set xmlSource to trim(xmlSource) | |
-- Get FileMaker data type | |
set fmDataType to fmDataTypeOfXML(xmlSource) | |
-- Read as appropriate data | |
if fmDataType is "FMLayoutObjects" then | |
set xmlData to readFileAsClass(filePath, «class XML2») | |
else if fmDataType is "FMScripts" then | |
set xmlData to readFileAsClass(filePath, «class XMSC») | |
else if fmDataType is "FMScriptSteps" then | |
set xmlData to readFileAsClass(filePath, «class XMSS») | |
else if fmDataType is "FMTables" then | |
set xmlData to readFileAsClass(filePath, «class XMTB») | |
else if fmDataType is "FMFields" then | |
set xmlData to readFileAsClass(filePath, «class XMFD») | |
else if fmDataType is "FMCustomFunctions" then | |
set xmlData to readFileAsClass(filePath, «class XMFN») | |
else | |
error "Invalid data type." | |
end if | |
-- Set the clipboard | |
set the clipboard to xmlData | |
on error eMsg number eNum | |
error "xmlFileToClipboard: " & eMsg number eNum | |
end try | |
end xmlFileToClipboard | |
on xmlToClipboard(xmlSource) | |
(* | |
Puts the specified XML to the clipboard for usage in FileMaker. | |
*) | |
try | |
-- Clean up XML | |
set xmlSource to trim(xmlSource) | |
-- Get FileMaker data type | |
set fmDataType to fmDataTypeOfXML(xmlSource) | |
-- Write XML data to temporary file | |
set tempFilePath to writeToTemporaryFileWithNameAndSuffix(xmlSource, false, ".xml") | |
-- Read as appropriate data | |
if fmDataType is "FMLayoutObjects" then | |
set xmlData to readFileAsClass(tempFilePath, «class XML2») | |
else if fmDataType is "FMScripts" then | |
set xmlData to readFileAsClass(tempFilePath, «class XMSC») | |
else if fmDataType is "FMScriptSteps" then | |
set xmlData to readFileAsClass(tempFilePath, «class XMSS») | |
else if fmDataType is "FMTables" then | |
set xmlData to readFileAsClass(tempFilePath, «class XMTB») | |
else if fmDataType is "FMFields" then | |
set xmlData to readFileAsClass(tempFilePath, «class XMFD») | |
else if fmDataType is "FMCustomFunctions" then | |
set xmlData to readFileAsClass(tempFilePath, «class XMFN») | |
else | |
error "Invalid data type." | |
end if | |
-- Delete temporary file | |
deleteFile(tempFilePath) | |
-- Set the clipboard | |
set the clipboard to xmlData | |
on error eMsg number eNum | |
try | |
-- Delete temporary file | |
deleteFile(tempFilePath) | |
end try | |
error "FMClipboardHelper/xmlToClipboard(): " & eMsg number eNum | |
end try | |
end xmlToClipboard | |
on __________________________________________CLIPBOARD_TO_FILE() | |
end __________________________________________CLIPBOARD_TO_FILE | |
on fmDataTypeFromClipboardAsFileWithName(fmDataType, fileName) | |
(* | |
Stores the XML data of the specified type found on the clipboard to a temporary file with the specified name. | |
*) | |
return fmDataTypesFromClipboardAsFileWithName({fmDataType}, fileName) | |
end fmDataTypeFromClipboardAsFileWithName | |
on fmDataTypesFromClipboardAsFileWithName(fmDataTypes, fileName) | |
(* | |
Stores the XML data of one of the specified types found on the clipboard to a temporary file with the specified name. | |
*) | |
return fmDataTypesFromClipboardAsFileWithNameInDirectory(fmDataTypes, fileName, false) | |
end fmDataTypesFromClipboardAsFileWithName | |
on fmDataTypeFromClipboardAsFileWithNameInDirectory(fmDataType, fileName, folderPath) | |
(* | |
Stores the XML data of the specified type found on the clipboard to a file with the specified name in the specified directory. | |
*) | |
return fmDataTypesFromClipboardAsFileWithNameInDirectory({fmDataType}, fileName, folderPath) | |
end fmDataTypeFromClipboardAsFileWithNameInDirectory | |
on fmDataTypesFromClipboardAsFileWithNameInDirectory(fmDataTypes, fileName, folderPath) | |
(* | |
Stores the XML data of one of the specified types found on the clipboard to a file with the specified name in the specified directory. | |
*) | |
try | |
-- Get the XML data from clipboard | |
try | |
set fmData to fmDataTypesFromClipboard(fmDataTypes) | |
on error eMsg number eNum | |
if eNum is 1000 then return "" | |
error eMsg number eNum | |
end try | |
-- Check file name | |
if fileName is false then | |
set fileName to fmDataTypeOfClipboard() | |
end if | |
if folderPath is false then | |
-- Create file in user's temporary items folder | |
set tempFilePath to writeToTemporaryFileWithNameAndSuffix(fmData, fileName, ".xml") | |
-- Format XML file | |
formatXMLAtPath(tempFilePath) | |
return tempFilePath | |
else | |
-- Convert folder path to HFS path | |
set folderPath to anyPathToHFSPath(folderPath) | |
-- Make sure the folder path ends with a colon | |
if folderPath does not end with ":" then | |
set folderPath to folderPath & ":" | |
end if | |
-- Generate file path | |
set filePath to folderPath & fileName & ".xml" | |
writeToFileAtPath(fmData, filePath) | |
-- Format XML file | |
formatXMLAtPath(filePath) | |
return filePath | |
end if | |
on error eMsg number eNum | |
error "fmDataTypesFromClipboardAsFileWithNameInDirectory: " & eMsg number eNum | |
end try | |
end fmDataTypesFromClipboardAsFileWithNameInDirectory | |
on __________________________________________CLIPBOARD_TO_XML() | |
end __________________________________________CLIPBOARD_TO_XML | |
on clipboardAsXML() | |
(* | |
Returns the XML found on the clipboard as text. | |
*) | |
return fmDataTypesFromClipboardAsXML(false) | |
end clipboardAsXML | |
on fmDataTypeFromClipboardAsXML(fmDataType) | |
(* | |
Returns the XML of the specified type found on the clipboard as text. | |
*) | |
return fmDataTypesFromClipboardAsXML({fmDataType}) | |
end fmDataTypeFromClipboardAsXML | |
on fmDataTypesFromClipboardAsXML(fmDataTypes) | |
(* | |
Returns the XML of one of the specified types found on the clipboard as text. | |
*) | |
try | |
-- Get the XML data from clipboard | |
try | |
set fmData to fmDataTypesFromClipboard(fmDataTypes) | |
on error eMsg number eNum | |
if eNum is 1000 then return "" | |
error eMsg number eNum | |
end try | |
set fmDataType to fmDataTypeOfClipboard() | |
set tempFilePath to writeToTemporaryFileWithNameAndSuffix(fmData, fmDataType, ".xml") | |
set fmXML to readFile(tempFilePath) | |
deleteFile(tempFilePath) | |
return fmXML | |
on error eMsg number eNum | |
error "fmDataTypesFromClipboardAsXML: " & eMsg number eNum | |
end try | |
end fmDataTypesFromClipboardAsXML | |
on ____________________________CLIPBOARD_TO_FORMATTED_XML() | |
end ____________________________CLIPBOARD_TO_FORMATTED_XML | |
on fmDataTypeFromClipboardAsFormattedXML(fmDataType) | |
(* | |
Returns the XML of the specified type found on the clipboard as formatted text. | |
*) | |
return fmDataTypesFromClipboardAsFormattedXML({fmDataType}) | |
end fmDataTypeFromClipboardAsFormattedXML | |
on fmDataTypesFromClipboardAsFormattedXML(fmDataTypes) | |
(* | |
Returns the XML of one of the specified types found on the clipboard as formatted text. | |
*) | |
try | |
-- Get the XML data from clipboard | |
try | |
set fmData to fmDataTypesFromClipboard(fmDataTypes) | |
on error eMsg number eNum | |
if eNum is 1000 then return "" | |
error eMsg number eNum | |
end try | |
set fmDataType to fmDataTypeOfClipboard() | |
set tempFilePath to writeToTemporaryFileWithNameAndSuffix(fmData, fmDataType, ".xml") | |
formatXMLAtPath(tempFilePath) | |
set fmXML to readFile(tempFilePath) | |
deleteFile(tempFilePath) | |
return fmXML | |
on error eMsg number eNum | |
error "fmDataTypesFromClipboardAsFormattedXML: " & eMsg number eNum | |
end try | |
end fmDataTypesFromClipboardAsFormattedXML | |
on _________________________________________CLIPBOARD_TO_DATA() | |
end _________________________________________CLIPBOARD_TO_DATA | |
on fmDataTypesFromClipboard(fmDataTypes) | |
(* | |
Returns the XML of one of the specified types found on the clipboard as data. | |
*) | |
try | |
if fmDataTypes is false then | |
set fmDataTypes to {"FMLayoutObjects", "FMScripts", "FMScriptSteps", "FMTables", "FMFields", "FMCustomFunctions"} -- return anyDataTypesFromClipboard() | |
end if | |
set clipboardContents to the clipboard | |
try | |
repeat with i from 1 to count of fmDataTypes | |
set fmDataType to item i of fmDataTypes | |
if fmDataType is "FMLayoutObjects" then | |
try | |
return «class XML2» of clipboardContents | |
end try | |
else if fmDataType is "FMScripts" then | |
try | |
return «class XMSC» of clipboardContents | |
end try | |
else if fmDataType is "FMScriptSteps" then | |
try | |
return «class XMSS» of clipboardContents | |
end try | |
else if fmDataType is "FMTables" then | |
try | |
return «class XMTB» of clipboardContents | |
end try | |
else if fmDataType is "FMFields" then | |
try | |
return «class XMFD» of clipboardContents | |
end try | |
else if fmDataType is "FMCustomFunctions" then | |
try | |
return «class XMFN» of clipboardContents | |
end try | |
else | |
error "Unknown data type. Available data types are: FMLayoutObjects, FMScripts, FMScriptSteps, FMTables, FMFields, and FMCustomFunctions" | |
end if | |
end repeat | |
error "No data type found." | |
on error eMsg number eNum | |
set fmDataType to listToText(fmDataTypes, ", ") | |
set dataTypesDescriptions to descriptionsForFMDataTypes(fmDataTypes) | |
set actualFMDataType to fmDataTypeOfClipboard() | |
set actualDataTypeDescription to descriptionForFMDataType(actualFMDataType) | |
if (count of fmDataTypes) is 1 then | |
set thisObjectString to "the correct objects" | |
else | |
set thisObjectString to "any of these objects" | |
end if | |
if actualFMDataType is "FMInvalidClipboard" then | |
-- Anything other than a record is on the clipboard | |
if fmDataType is "FMLayoutObjects" then | |
-- Layout objects were requested | |
set eMsg to "Could not get " & listToText(dataTypesDescriptions, " or ") & " from clipboard. Please make sure to actually copy " & thisObjectString & " to the clipboard. This error usually occurs when the copy command is performed while the Inspector panel is the frontmost window." | |
else | |
-- Anything other than layout objects were requested | |
set eMsg to "Could not get " & listToText(dataTypesDescriptions, " or ") & " from clipboard. Please make sure to actually copy " & thisObjectString & " to the clipboard." | |
end if | |
else if actualFMDataType is "FMInvalidDataType" then | |
-- An unknown data type was found on the clipboard | |
set eMsg to "Could not get " & listToText(dataTypesDescriptions, " or ") & " from clipboard. What was copied is not supported by this script." | |
else | |
-- The wrong data type was found on the clipboard | |
set eMsg to "The clipboard contains " & actualDataTypeDescription & " not " & listToText(dataTypesDescriptions, " or ") & "." | |
end if | |
activate | |
display alert "Clipboard Error" message eMsg as critical | |
error eMsg number 1000 | |
end try | |
on error eMsg number eNum | |
error "fmDataTypesFromClipboard: " & eMsg number eNum | |
end try | |
end fmDataTypesFromClipboard | |
on anyDataTypesFromClipboard() | |
(* | |
Returns the XML found on the clipboard as data. | |
*) | |
try | |
set fmDataType to fmDataTypeOfClipboard() | |
set clipboardContents to the clipboard | |
if fmDataType is "FMLayoutObjects" then | |
return «class XML2» of clipboardContents | |
else if fmDataType is "FMScripts" then | |
return «class XMSC» of clipboardContents | |
else if fmDataType is "FMScriptSteps" then | |
return «class XMSS» of clipboardContents | |
else if fmDataType is "FMTables" then | |
return «class XMTB» of clipboardContents | |
else if fmDataType is "FMFields" then | |
return «class XMFD» of clipboardContents | |
else if fmDataType is "FMCustomFunctions" then | |
return «class XMFN» of clipboardContents | |
else | |
error "Unknown data type. Available data types are: FMLayoutObjects, FMScripts, FMScriptSteps, FMTables, FMFields, and FMCustomFunctions" | |
end if | |
on error eMsg number eNum | |
error "anyDataTypesFromClipboard: " & eMsg number eNum | |
end try | |
end anyDataTypesFromClipboard | |
on __________________________________________________DATA_TYPES() | |
end __________________________________________________DATA_TYPES | |
on fmDataTypeOfClipboard() | |
(* | |
Returns the data type of the current clipboard. | |
*) | |
set clipboardContents to the clipboard | |
set clipboardClass to (class of clipboardContents) as text | |
if clipboardClass is not "record" then | |
-- The data on the clipboard has the wrong class | |
return "FMInvalidClipboard" | |
else | |
try | |
-- The following line produces a text representation of the record as error message | |
get item 0 of clipboardContents | |
on error eMsg | |
-- Parse the error message to get the data class | |
set o to offset of "«class " in eMsg | |
if o is 0 then | |
-- The text "«class " was not found | |
return "FMInvalidClipboard" | |
end if | |
-- Get a substring from the error message representing the class name | |
set fmDataClassName to text (o + 7) thru (o + 10) of eMsg | |
-- Convert class name to data type | |
set fmDataType to fmDataClassNameToDataType(fmDataClassName) | |
return fmDataType | |
end try | |
end if | |
end fmDataTypeOfClipboard | |
on fmDataTypeOfXML(xmlSource) | |
try | |
set prvDlmt to text item delimiters | |
try | |
set text item delimiters to "<fmxmlsnippet" | |
set tempString to text item 2 of xmlSource | |
set text item delimiters to "<" | |
set tempString to text item 2 of tempString | |
set o1 to offset of ">" in tempString | |
set o2 to offset of " " in tempString | |
if o1 < o2 then | |
set text item delimiters to ">" | |
else | |
set text item delimiters to " " | |
end if | |
set dataType to text item 1 of tempString | |
on error | |
set dataType to false | |
end try | |
set text item delimiters to prvDlmt | |
if dataType is "Layout" then | |
return "FMLayoutObjects" | |
else if dataType is "Script" then | |
return "FMScripts" | |
else if dataType is "Step" then | |
return "FMScriptSteps" | |
else if dataType is "BaseTable" then | |
return "FMTables" | |
else if dataType is "Field" then | |
return "FMFields" | |
else if dataType is "CustomFunction" then | |
return "FMCustomFunctions" | |
else | |
return "FMInvalidDataType" | |
end if | |
on error eMsg number eNum | |
error "fmDataTypeOfXML: " & eMsg number eNum | |
end try | |
end fmDataTypeOfXML | |
on fmDataClassNameToDataType(fmDataClassName) | |
(* | |
Converts a data class name to its data type. | |
*) | |
if fmDataClassName is "XML2" then | |
return "FMLayoutObjects" | |
else if fmDataClassName is "XMSC" then | |
return "FMScripts" | |
else if fmDataClassName is "XMSS" then | |
return "FMScriptSteps" | |
else if fmDataClassName is "XMTB" then | |
return "FMTables" | |
else if fmDataClassName is "XMFD" then | |
return "FMFields" | |
else if fmDataClassName is "XMFN" then | |
return "FMCustomFunctions" | |
else | |
return "FMInvalidDataType" | |
end if | |
end fmDataClassNameToDataType | |
on descriptionsForFMDataTypes(fmDataTypes) | |
(* | |
Returns a list of descriptions for the specified data types. | |
*) | |
set theDescriptions to {} | |
repeat with i from 1 to count of fmDataTypes | |
set end of theDescriptions to descriptionForFMDataType(item i of fmDataTypes) | |
end repeat | |
return theDescriptions | |
end descriptionsForFMDataTypes | |
on descriptionForFMDataType(fmDataType) | |
(* | |
Returns a description for the specified data type. | |
*) | |
if fmDataType is "FMLayoutObjects" then | |
return "layout objects" | |
else if fmDataType is "FMScripts" then | |
return "scripts" | |
else if fmDataType is "FMScriptSteps" then | |
return "script steps" | |
else if fmDataType is "FMTables" then | |
return "tables" | |
else if fmDataType is "FMFields" then | |
return "fields" | |
else if fmDataType is "FMCustomFunctions" then | |
return "custom functions" | |
else | |
return "unknown type" | |
end if | |
end descriptionForFMDataType | |
on _________________________________________________FILE_SYSTEM() | |
end _________________________________________________FILE_SYSTEM | |
on writeToTemporaryFileWithNameAndSuffix(content, fileName, theSuffix) | |
(* | |
Writes content to a file with the specified name and suffix to the user's temporary items folder. | |
*) | |
try | |
-- Generate temporary file path | |
set tempFilePath to temporaryUniquePathWithNameAndSuffix(fileName, theSuffix) | |
-- Write to temporary file | |
writeToFileAtPath(content, tempFilePath) | |
return tempFilePath | |
on error eMsg number eNum | |
error "writeToTemporaryFileWithNameAndSuffix: " & eMsg number eNum | |
end try | |
end writeToTemporaryFileWithNameAndSuffix | |
on writeToFileAtPath(content, filePath) | |
(* | |
Writes content to the file at the specified path. | |
*) | |
try | |
-- Convert file path to HFS path | |
set filePath to anyPathToHFSPath(filePath) | |
-- Open file | |
try | |
open for access file filePath with write permission | |
on error eMsg number eNum | |
error "Could not open file with write permission: " & eMsg number eNum | |
end try | |
-- Write to file | |
try | |
set fileEnd to 0 | |
set eof of file filePath to fileEnd | |
set writeData to content | |
try | |
write writeData to file filePath starting at fileEnd as «class utf8» | |
on error | |
write writeData to file filePath starting at fileEnd | |
end try | |
on error eMsg number eNum | |
try | |
close access file filePath | |
end try | |
error "Error while writing to file: " & eMsg number eNum | |
end try | |
-- Close file | |
try | |
close access file filePath | |
end try | |
return true | |
on error eMsg number eNum | |
error "writeToFileAtPath: " & eMsg number eNum | |
end try | |
end writeToFileAtPath | |
on readFile(filePath) | |
(* | |
Returns the contents of a file as UTF-8 text. | |
*) | |
try | |
readFileAsClass(filePath, «class utf8») | |
on error eMsg number eNum | |
error "readFile: " & eMsg number eNum | |
end try | |
end readFile | |
on readFileAsClass(filePath, contentClass) | |
(* | |
Returns the contents of a file as the specified class. | |
*) | |
try | |
-- Convert file path to HFS path | |
set filePath to anyPathToHFSPath(filePath) | |
-- Open file for reading | |
try | |
open for access file filePath | |
on error eMsg number eNum | |
error "Could not open file: " & eMsg number eNum | |
end try | |
-- Read | |
try | |
set fileContents to read file filePath as contentClass | |
on error eMsg number eNum | |
try | |
close access file filePath | |
end try | |
error "Error while trying to read file: " & eMsg number eNum | |
end try | |
-- Close | |
try | |
close access file filePath | |
end try | |
return fileContents | |
on error eMsg number eNum | |
set eMsg to "readFile: " & eMsg | |
error eMsg number eNum | |
end try | |
end readFileAsClass | |
on deleteFile(filePath) | |
(* | |
Deletes the file at the specified path. | |
*) | |
try | |
if existsFile(filePath) is false then return | |
tell application "System Events" | |
delete file filePath | |
end tell | |
on error eMsg number eNum | |
do shell script "/bin/rm -f " & qpp(filePath) | |
end try | |
end deleteFile | |
on existsFile(filePath) | |
(* | |
Checks if a file at the specified path exists. | |
*) | |
try | |
-- Convert file path to HFS path | |
set filePath to anyPathToHFSPath(filePath) | |
tell application "System Events" to return (exists file filePath) | |
on error eMsg number eNum | |
error "existsFile: " & eMsg number eNum | |
end try | |
end existsFile | |
on makeDirectory(parentPath, directoryName) | |
(* | |
Creates a folder with the specified name in a folder at the specified path. | |
*) | |
try | |
-- Convert parent path to HFS path | |
set parentPath to anyPathToHFSPath(parentPath) | |
if parentPath does not end with ":" then | |
set parentPath to parentPath & ":" | |
end if | |
-- Generate full directory path | |
set directoryPath to parentPath & directoryName & ":" | |
tell application "System Events" | |
if (exists folder directoryPath) is false then | |
-- Directory does not exist; create it | |
make new folder at the end of folders of folder parentPath with properties {name:directoryName} | |
end if | |
end tell | |
return directoryPath | |
on error eMsg number eNum | |
error "makeDirectory: " & eMsg number eNum | |
end try | |
end makeDirectory | |
on temporaryPathWithNameAndSuffix(fileName, theSuffix) | |
(* | |
Creates a temporary file path with the specified name and suffix. | |
*) | |
try | |
-- Add dot to suffix | |
if character 1 of theSuffix is not "." then | |
set theSuffix to "." & theSuffix | |
end if | |
-- Get the path to the temporary items folder | |
set tempItemsDirectoryPath to (path to temporary items folder from user domain) as text | |
-- Make sure the clipboard helper's directory exists | |
set parentFolderPath to makeDirectory(tempItemsDirectoryPath, "FMClipboardHelper") | |
return parentFolderPath & fileName & theSuffix | |
on error eMsg number eNum | |
error "temporaryPathWithNameAndSuffix: " & eMsg number eNum | |
end try | |
end temporaryPathWithNameAndSuffix | |
on temporaryUniquePathWithNameAndSuffix(fileName, theSuffix) | |
(* | |
Creates a temporary unique file path. Uses fileName as base name if not set to false. Uses theSuffix as suffix if not set to false. | |
*) | |
try | |
if fileName is false then | |
-- No file name specified; generate one | |
-- Generate pseudorandom numbers | |
set rand1 to (round (random number from 100 to 999)) as text | |
set rand2 to (round (random number from 100 to 999)) as text | |
set rand3 to (round (random number from 100 to 999)) as text | |
set randomText to rand1 & "-" & rand2 & "-" & rand3 | |
-- Generate full file name | |
set fileName to (("AppleScriptTempFile_" & randomText) as text) | |
end if | |
if theSuffix is not false then | |
-- Add dot to suffix | |
if character 1 of theSuffix is not "." then | |
set theSuffix to "." & theSuffix | |
end if | |
else | |
set theSuffix to "" | |
end if | |
-- Get the path to the temporary items folder | |
set tempItemsDirectoryPath to (path to temporary items folder from user domain) as text | |
-- Make sure the clipboard helper's directory exists | |
set parentFolderPath to makeDirectory(tempItemsDirectoryPath, "FMClipboardHelper") | |
-- Make sure the file does not exist | |
set rNumber to 1 | |
repeat | |
if rNumber is 1 then | |
set tempFilePath to parentFolderPath & fileName & theSuffix | |
else | |
set tempFilePath to parentFolderPath & fileName & "_" & (rNumber as text) & theSuffix | |
end if | |
tell application "System Events" | |
if (exists file tempFilePath) is false then | |
exit repeat | |
end if | |
end tell | |
set rNumber to rNumber + 1 | |
end repeat | |
return tempFilePath | |
on error eMsg number eNum | |
error "temporaryUniquePathWithNameAndSuffix: " & eMsg number eNum | |
end try | |
end temporaryUniquePathWithNameAndSuffix | |
on qpp(aPath) | |
(* | |
Returns a quoted posix path of the item at the specified path. | |
*) | |
return quoted form of (POSIX path of anyPathToHFSPath(aPath)) | |
end qpp | |
on anyPathToHFSPath(aPath) | |
(* | |
Converts any path to a HFS path. | |
*) | |
try | |
-- Expand tilde in path | |
if aPath starts with "~" then | |
-- Get the path to the user’s home folder | |
set userPath to POSIX path of (path to home folder) | |
-- Remove trailing slash | |
if userPath ends with "/" then set userPath to text 1 thru -2 of userPath as text | |
if aPath is "~" then | |
set aPath to userPath | |
else | |
set aPath to userPath & text 2 thru -1 of aPath as text | |
end if | |
end if | |
-- Convert to HFS style path if necessary | |
if aPath does not contain ":" then set aPath to (POSIX file aPath) as text | |
return aPath | |
on error eMsg number eNum | |
error "anyPathToHFSPath: " & eMsg number eNum | |
end try | |
end anyPathToHFSPath | |
on ________________________________________________________TEXT() | |
end ________________________________________________________TEXT | |
on formatXMLAtPath(filePath) | |
(* | |
Formats an XML file in place at the specified path. | |
*) | |
try | |
do shell script "/usr/bin/xmllint --format --output " & qpp(filePath) & " " & qpp(filePath) | |
on error eMsg number eNum | |
error "formatXMLAtPath: " & eMsg number eNum | |
end try | |
end formatXMLAtPath | |
on snr(aText, aPattern, aReplacement) | |
(* | |
Searches and replaces a pattern in the specified text. | |
*) | |
try | |
if (class of aPattern) is list and (class of aReplacement) is list then | |
if (count of aPattern) is not (count of aReplacement) then error "The count of patterns and replacements needs to match." | |
-- Process matching list of patterns and replacements | |
repeat with i from 1 to count of aPattern | |
set aText to snr(aText, item i of aPattern, item i of aReplacement) | |
end repeat | |
return aText | |
else if class of aPattern is list then | |
-- Replace multiple patterns with the same text | |
repeat with i from 1 to count of aPattern | |
set aText to snr(aText, item i of aPattern, aReplacement) | |
end repeat | |
return aText | |
end if | |
-- Return text early if it doesn't contain the pattern | |
if aText does not contain aPattern then return aText | |
set prvDlmt to text item delimiters | |
try | |
set text item delimiters to aPattern | |
set tempList to text items of aText | |
set text item delimiters to aReplacement | |
set aText to tempList as text | |
end try | |
set text item delimiters to prvDlmt | |
return aText | |
on error eMsg number eNum | |
error "snr: " & eMsg number eNum | |
end try | |
end snr | |
on textToList(aString, aDelimiter) | |
(* | |
Converts a text to list using the specified delimiter. | |
*) | |
try | |
set prvDlmt to text item delimiters | |
set text item delimiters to aDelimiter | |
set aList to text items of aString | |
set text item delimiters to prvDlmt | |
return aList | |
on error eMsg number eNum | |
error "textToList: " & eMsg number eNum | |
end try | |
end textToList | |
on listToText(aList, aDelimiter) | |
(* | |
Converts a list to a text using the specified delimiter. | |
*) | |
try | |
set prvDlmt to text item delimiters | |
set text item delimiters to aDelimiter | |
set aString to aList as text | |
set text item delimiters to prvDlmt | |
return aString | |
on error eMsg number eNum | |
error "listToText: " & eMsg number eNum | |
end try | |
end listToText | |
on sortList(aList) | |
(* | |
Sorts a list of strings. | |
*) | |
try | |
set aText to listToText(aList, ASCII character 10) | |
return paragraphs of (do shell script "/bin/echo " & quoted form of aText & " | /usr/bin/sort") | |
on error eMsg number eNum | |
error "sortList: " & eMsg number eNum | |
end try | |
end sortList | |
on countMatchesInList(aString, aList) | |
(* | |
Counts the matches of a string in a list. | |
*) | |
try | |
set matchCount to 0 | |
repeat with i from 1 to count of aList | |
if item i of aList is aString then | |
set matchCount to matchCount + 1 | |
end if | |
end repeat | |
return matchCount | |
on error eMsg number eNum | |
error "uniqueWordsStartingWith: " & eMsg number eNum | |
end try | |
end countMatchesInList | |
on uniqueWordsStartingWith(aText, aPrefix) | |
(* | |
Finds all words that start with a prefix in a text. Returns only the first occurrence of each word. | |
*) | |
try | |
set wordList to textToList(aText, aPrefix) | |
if (count of wordList) is 1 then return {} | |
set uniqueWords to {} | |
repeat with i from 2 to count of wordList | |
set thisWord to aPrefix & first word of item i of wordList | |
if thisWord is not in uniqueWords then | |
set end of uniqueWords to thisWord | |
end if | |
end repeat | |
set uniqueWords to sortList(uniqueWords) | |
return uniqueWords | |
on error eMsg number eNum | |
error "uniqueWordsStartingWith: " & eMsg number eNum | |
end try | |
end uniqueWordsStartingWith | |
on wordsStartingWith(aText, aPrefix) | |
(* | |
Finds all words that start with a prefix in a text. | |
*) | |
try | |
set wordList to textToList(aText, aPrefix) | |
if (count of wordList) is 1 then return {} | |
set allWords to {} | |
repeat with i from 2 to count of wordList | |
set thisWord to aPrefix & first word of item i of wordList | |
set end of allWords to thisWord | |
end repeat | |
set allWords to sortList(allWords) | |
return allWords | |
on error eMsg number eNum | |
error "wordsStartingWith: " & eMsg number eNum | |
end try | |
end wordsStartingWith | |
on trim(aText) | |
(* | |
Strips a text of its surrounding white space. | |
*) | |
try | |
(* Strip leading white space *) | |
if aText is "" then return aText | |
-- Initialize the collector variable | |
set leadingWhiteSpace to "" | |
repeat with i from 1 to count of characters in aText | |
-- Is this character still whitespace? | |
if (ASCII number (character i of aText)) > 32 and (ASCII number (character i of aText)) ≠ 202 then | |
exit repeat -- No more white space; stop collecting it | |
else | |
-- Collect white space | |
set leadingWhiteSpace to leadingWhiteSpace & character i of aText | |
end if | |
end repeat | |
-- Mark the beginning of the text | |
set aText to "#!#BEGINNING#!#" & aText | |
-- Get the text after the leading white space | |
set prvDlmt to text item delimiters | |
set text item delimiters to "#!#BEGINNING#!#" & leadingWhiteSpace | |
set aText to text item 2 of aText | |
set text item delimiters to prvDlmt | |
(* Strip trailing white space *) | |
if aText is "" then return aText | |
-- Initialize the collector variable | |
set trailingWhiteSpace to "" | |
-- Define the last character to start at | |
set i to count of characters in aText | |
repeat | |
-- Is this character still whitespace? | |
if (ASCII number (character i of aText)) > 32 and (ASCII number (character i of aText)) ≠ 202 then | |
exit repeat -- No more white space; stop collecting it | |
else | |
-- Collect white space | |
set trailingWhiteSpace to character i of aText & trailingWhiteSpace | |
set i to i - 1 | |
end if | |
end repeat | |
-- Is there any white space? | |
if (count of characters in trailingWhiteSpace) is 0 then return aText | |
-- Mark the end of the text | |
set aText to aText & "#!#THE_END#!#" | |
-- Get the part of the text before the trailing white space | |
set prvDlmt to text item delimiters | |
set text item delimiters to trailingWhiteSpace & "#!#THE_END#!#" | |
set aText to text item 1 of aText | |
set text item delimiters to prvDlmt | |
return aText | |
on error eMsg number eNum | |
error "trim: " & eMsg number eNum | |
end try | |
end trim |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment