"Get super classes" 1.2

I updated the “Get super classes” 1.1.1. The link to the previous “Get super classes” is shown below. The notes in the previous release notes all apply to the new version as well.
[](New version of "Get super classes")

The new version “Get super classes 1.2” also returns the framework the class is contained in. The framework name appears directly above the class hierarchy. Other then that it works exactly the same.

If the applet gets an error determining the Framework name it will display just the class hierarchy.

Foundation, AppKit, Quartz, and Carbon are framework that come up often. Occasionally other frameworks come up as well. If you see a class in some sample code you can look up both it’s superclasses and the Framework the class it’s in with this applet.

The source code is listed below:

use scripting additions

use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "Carbon"


on GetStringState(TheString)
	-- Make sure TheString is really a string
	set TheString to TheString as text
	
	set LowercaseFound to false
	set UpperCaseFound to false
	
	set StringCharacter to characters of TheString
	repeat with TheCharacter in StringCharacter
		if (not LowercaseFound) then
			set LowercaseFound to ((id of "a") ≤ (id of TheCharacter)) and ((id of TheCharacter) ≤ (id of "z"))
		end if
		if (not UpperCaseFound) then
			set UpperCaseFound to ((id of "A") ≤ (id of TheCharacter)) and ((id of TheCharacter) ≤ (id of "Z"))
		end if
	end repeat
	
	if (UpperCaseFound and LowercaseFound) then set State to "MixedCase"
	if (not UpperCaseFound and not LowercaseFound) then set State to "NoLettersFound"
	if (UpperCaseFound and not LowercaseFound) then set State to "AllUpperCase"
	if (not UpperCaseFound and LowercaseFound) then set State to "AllLowerCase"
	return State
end GetStringState

on GetClassNameAsString(TheClass)
	-- Returns a string representation of the given class.
	return (current application's NSStringFromClass(TheClass's |class|())) as text
end GetClassNameAsString

on GetInheritancePath(CurrentClassName)
	script ReturnObj
		property Successful : false
		property ClassPath : ""
	end script
	
	local CurrentClass
	local ClassPath
	local NextClass
	local TheClass
	
	try
		if (CurrentClassName = "NSObject") then return CurrentClassName
		
		-- Create the class indicated by the given string.  This needs to be done first so
		-- later on the "super class" of the "current class" can be found.
		-- If the the string entered by the user does not equate to a class then 
		-- NSClassFromString returns missing value.
		set CurrentClass to current application's NSClassFromString(CurrentClassName)
		
		if (CurrentClass = missing value) then
			-- A valid class name was not entered
			if (CurrentClassName = "") then
				display dialog "No characters were entered as input." buttons {"OK"} default button "OK" with title "Error"
			else if ((length of CurrentClassName) < 4) then
				display dialog "Invalid input.  Only " & (length of CurrentClassName) & " character(s) was input" buttons {"OK"} default button "OK" with title "Error"
			else
				set StringStatus to GetStringState(CurrentClassName)
				if (StringStatus = "AllUpperCase") then
					display dialog "Invalid input.  All the text in the input, \"" & CurrentClassName & ",\" was all upper case." buttons {"OK"} default button "OK" with title "Error"
				else if (StringStatus = "AllLowerCase") then
					display dialog "Invalid input.  All the text in the input, \"" & CurrentClassName & ",\" was all lower case." buttons {"OK"} default button "OK" with title "Error"
				else if (StringStatus = "NoLettersFound") then
					display dialog "Invalid input.  There were no letters found in the input, \"" & CurrentClassName & "\"" buttons {"OK"} default button "OK" with title "Error"
				else
					display dialog "Invalid input.  The entered text \"" & CurrentClassName & ¬
						"\" does not appear to represent a valid class name." buttons {"OK"} default button "OK" with title "Error"
				end if
			end if
			set (Successful of ReturnObj) to false
			return ReturnObj
		end if
		
		set ClassPath to CurrentClassName
		
		-- This loop keeps getting the suppclass of the the current class until
		-- the current class is NSObject.  All class inherit from NSObject.
		repeat until (GetClassNameAsString(CurrentClass) = "NSObject")
			-- Get the parent class of the CurrentClass
			set NextClass to CurrentClass's superclass()
			
			-- Convert the class of NextClass to a string equivalent so
			-- the text can be can be appended to the ClassPath string and
			-- can be compared again at the top loop.
			set ClassName to GetClassNameAsString(NextClass)
			
			-- Add newest string representation of the parent class to the
			-- ClassPath.
			set ClassPath to ClassName & "-->" & ClassPath
			
			-- Start loop again with a new CurrentClass
			set CurrentClass to NextClass
		end repeat
		
		set (ClassPath of ReturnObj) to ClassPath
		set (Successful of ReturnObj) to true
		return ReturnObj
	on error errMsg number errNum
		display dialog "Error " & (errNum as string) ¬
			& " occured getting the inheritance path." & return & return & errMsg with title "Error" buttons {"OK"} default button "OK"
		set (Successful of ReturnObj) to false
		return ReturnObj
	end try
end GetInheritancePath

set UserEntry to display dialog ¬
	"The class will be converted to an inheritance path" & return & ¬
	"The input is case sensitive." with title "Enter a class" buttons {"OK", "Cancel"} default button "OK" default answer ""

set StartingClass to text returned of UserEntry

if (StartingClass = "NSObject") then
	display dialog ¬
		"The arrow points to the class that is inheriting from its parent." with title ¬
		"inheritance path" buttons {"OK"} default button "OK" cancel button "OK" default answer "Framework: Foundation" & return & "NSObject"
else
	set TheResult to GetInheritancePath(StartingClass as text)
	
	if (Successful of TheResult) then
		try
			-- Calculate the name of the Framework
			set TheClass to current application's NSClassFromString(StartingClass as text)
			set TheName to (((current application's NSBundle's bundleForClass:TheClass)'s bundleURL)'s lastPathComponent()) as string
			if (TheName ends with ".framework") then
				set TheName to strings 1 through -11 of TheName
			end if
			
			display dialog ¬
				"The arrow points to the class that is inheriting from its parent." with title ¬
				"inheritance path" buttons {"OK"} default button "OK" default answer "Framework: " & TheName & return & (ClassPath of TheResult)
		on error errMsg number errNum
			-- If an error occurs calculating the framework name  then leave the framework name out
			display dialog ¬
				"The arrow points to the class that is inheriting from its parent." with title ¬
				"inheritance path" buttons {"OK"} default button "OK" cancel button "OK" default answer (ClassPath of TheResult)
		end try
	else
		return false
	end if
end if

Bill

Get super classes 1.2.app.zip (66.7 KB)

FWIW, a footnote – a stringCase assembled from construction bricks:

-- String -> (any Upper ?, any Lower ?)
-- stringCase :: String -> (Bool, Bool)
on stringCase(s)
    script
        on |λ|(accumulator, c)
            set {u, l} to accumulator
            {u or isUpper(c), l or isLower(c)}
        end |λ|
    end script
    foldl(result, {false, false}, s)
end stringCase

-- TEST ------------------------------------------------------------------
on run
    map(stringCase, {"NSString", "NSURL", "123", "int"})
    
    --> {{true, true}, {true, false}, {false, false}, {false, true}} 
end run


-- GENERICS --------------------------------------------------------------

-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from 1 to lng
            set v to |λ|(v, item i of xs, i, xs)
        end repeat
        return v
    end tell
end foldl

-- isLower :: Char -> Bool
on isLower(c)
    set d to (id of c) - 97 -- id of "a"
    d ≥ 0 and d < 26
end isLower

-- isUpper :: Char -> Bool
on isUpper(c)
    set d to (id of c) - 65 -- id of "A"
    d ≥ 0 and d < 26
end isUpper

-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map

-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

A Keyboard Maestro route to side-stepping the problem of case-sensitivity
(by generating a list of class names, and providing browsing or predictive typing)

(Done in JavaScript for Automation, which provides additional tools for working with ObjC, though perhaps there is also a route to doing this kind of thing through AppleScript ?)

Or, of course, we could generate an A-Z index:
NSandCFClassPaths.txt.zip (16.9 KB)

1 Like

Just be aware that introspecting the run-time returns all classes registered with it — that includes Apple private classes, plus all classes defined in the host application. So it’s kind of interesting stuff to look at, but you need to be very careful if you’re actually using it.

( I actually used run-time introspection in my first attempt at code-completion in ASObjC Explorer; it’s a heck of a lot easier (and faster) than parsing the header files, and you can glean an awful lot of information from the run-time. Way too much, it turned out — the result was awful.)

2 Likes

the result was awful

: - )

Can certainly be overkill – useful though, in this limited case, I think.

(Protects us from having to think about case, and lets us get a bit quicker to the full name-string).

1 Like