Major fix to documentation generation:

- It turns out doxygen would not generate documentation for the first method in a file (or sometimes any methods at all) if there was no ivar block or an empty ivar block. Now a dummy __doxygen__ ivar is inserted in empty blocks to force correct behavior.

- Added a missing @endverbatim to CPExpression.j.

- Tweaked class documentation for CPPredicate.j.
This commit is contained in:
Aparajita Fishman
2011-05-19 16:46:41 -07:00
parent a435e54a67
commit ff7774976e
4 changed files with 111 additions and 77 deletions
+15 -7
View File
@@ -71,9 +71,12 @@ CPMinusSetExpressionType = 9;
@class CPExpression
@brief CPExpression is used to represent expressions in a predicate.
Comparison operations in an CPPredicate are based on two expressions, as represented by instances of the CPExpression class. Expressions are created for constant values, key paths, and so on.
Comparison operations in an CPPredicate are based on two expressions, as represented by instances of the CPExpression class.
Expressions are created for constant values, key paths, and so on.
Generally, anywhere in the CPExpression class hierarchy where there is composite API and subtypes that may only reasonably respond to a subset of that API, invoking a method that does not make sense for that subtype will cause an exception to be thrown.
Generally, anywhere in the CPExpression class hierarchy where there is composite API and subtypes
that may only reasonably respond to a subset of that API, invoking a method that does not make sense
for that subtype will cause an exception to be thrown.
*/
@implementation CPExpression : CPObject
@@ -182,12 +185,15 @@ CPMinusSetExpressionType = 9;
Returns a new expression that will invoke one of the predefined functions.
@param function_name The name of the function to invoke.
@param parameters An array containing CPExpression objects that will be used as parameters during the invocation of selector.
For a selector taking no parameters, the array should be empty. For a selector taking one or more parameters, the array should contain one CPExpression object which will evaluate to an instance of the appropriate type for each parameter.
If there is a mismatch between the number of parameters expected and the number you provide during evaluation, an exception may be raised or missing parameters may simply be replaced by nil (which occurs depends on how many parameters are provided, and whether you have over- or underflow).
@return A new expression that invokes the function name using the parameters in parameters.
For a selector taking no parameters, the array should be empty. For a selector taking one or more parameters,
the array should contain one CPExpression object which will evaluate to an instance of the appropriate type for each parameter.
If there is a mismatch between the number of parameters expected and the number you provide during evaluation,
an exception may be raised or missing parameters may simply be replaced by nil (which occurs depends on how many
parameters are provided, and whether you have over- or underflow).
The name parameter can be one of the following predefined functions:
@verbatim
name parameter array contents returns
@@ -220,6 +226,7 @@ CPMinusSetExpressionType = 9;
now: none [CPDate now]
This method raises an exception immediately if the selector is invalid; it raises an exception at runtime if the parameters are incorrect.
@endverbatim
*/
+ (CPExpression)expressionForFunction:(CPString)function_name arguments:(CPArray)parameters
{
@@ -232,7 +239,8 @@ CPMinusSetExpressionType = 9;
@param selectorName The name of the method to be invoked.
@param parameters An array containing CPExpression objects which can be evaluated to provide parameters for the method specified by name.
@return An expression which will return the result of invoking the selector named name on the result of evaluating the target expression with the parameters specified by evaluating the elements of parameters.
See the description of expressionForFunction:arguments: for examples of how to construct the parameter array.
See the description of \c expressionForFunction:arguments: for examples of how to construct the parameter array.
*/
+ (CPExpression)expressionForFunction:(CPExpression)target selectorName:(CPString)selectorName arguments:(CPArray)parameters
{
+1 -1
View File
@@ -55,7 +55,7 @@
You can create predicates for operations, such as <code>\@sum.items.price < 1000</code>.
You can also create predicates that include variables, so that the predicate can be pre-defined before
substituting concrete values at runtime with the <code>evaluateWithObject:substitutionVariables:</code> method.
substituting concrete values at runtime with the \c evaluateWithObject:substitutionVariables: method.
*/
@implementation CPPredicate : CPObject
@@ -7,7 +7,7 @@
ACCESSOR_GET_TEMPLATE = <<EOS
/*!
Synthesized accessor method.
Synthesized accessor method.
*/
- (\#{ivarType})\#{getter}
{
@@ -16,9 +16,11 @@ ACCESSOR_GET_TEMPLATE = <<EOS
EOS
ACCESSOR_GET_INTERFACE_TEMPLATE = "- (\#{ivarType})\#{getter}"
ACCESSOR_SET_TEMPLATE = <<EOS
/*!
Synthesized accessor method.
Synthesized accessor method.
*/
- (void)\#{setter}:(\#{ivarType})aValue
{
@@ -27,6 +29,8 @@ ACCESSOR_SET_TEMPLATE = <<EOS
EOS
ACCESSOR_SET_INTERFACE_TEMPLATE = "- (void)\#{setter}:(\#{ivarType})aValue"
ACCESSORS_IMPLEMENTATION_TEMPLATE = <<EOS
@implementation \#{className} (CPSynthesizedAccessors)
@@ -34,109 +38,129 @@ ACCESSORS_IMPLEMENTATION_TEMPLATE = <<EOS
\#{accessorsSource}@end
EOS
DUMMY_IVAR = " id __doxygen__;"
def makeHeaderFileFrom(fileName)
# Grab the entire file (text)
sourceFile = File.new(fileName, "r")
source = sourceFile.read
sourceFile.close()
# Grab the entire file (text)
sourceFile = File.new(fileName, "r")
source = sourceFile.read
sourceFile.close()
# Remove @accessor declarations from ivars in the source file
sourceFile = File.new(fileName, "w")
sourceFile.write(source.gsub(/(\s*\w+\s+\w+)\s+@accessors(\(.+?\))?;/m, "\\1;"))
# Remove @accessor declarations from ivars in the source file
source = source.gsub(/(\s*\w+\s+\w+)\s+@accessors(\(.+?\))?;/m, "\\1;")
# Extract all the @implementations blocks. Note, there may be
# more than on in a given .j file.
m = source.scan(/^\s*(@implementation\s*(\w+)\s*:\s*\w+\s*\{(.*?)\}).*?^\s*@end\s*$/m)
# If an implementation does not have an ivar block or an empty ivar block,
# an one with a dummy ivar so that doxygen will parse the file correctly.
source.gsub!(/^\s*(@implementation \s*\w+(?:\s*:\s*\w+)?)\n(\s*[^{])/, "\\1\n{\n#{DUMMY_IVAR}\n}\n\\2")
source.gsub!(/^\s*(@implementation \s*\w+(?:\s*:\s*\w+)?)\n\s*\{\s*\}/, "\\1\n{\n#{DUMMY_IVAR}\n}")
return if m.length == 0
# If there is
sourceFile = File.new(fileName, "w")
sourceFile.write(source)
for i in 0...m.length
groups = m[i]
declaration = groups[0]
className = groups[1]
ivars = groups[2]
# Extract all the @implementations blocks. Note, there may be more than one in a given .j file.
m = source.scan(/^\s*(@implementation\s*(\w+)\s*(?::\s*\w+)?)\s*(?:\{(.*?)\})?(.*?)^\s*@end\s*$/m)
# Change "implementation" to "interface", create the .h file, and write the interface
newDeclaration = declaration.sub("@implementation", "@interface")
newFileName = File.dirname(fileName) + "/" + className + ".h"
f = File.new(newFileName, "a")
return if m.length == 0
# Change @accessors declarations to a comment, doxygen chokes on them
f.write("\n" + newDeclaration.gsub(/(\s*\w+\s+\w+)\s+(@accessors.*?);/m, "\\1; // \\2") + "\n@end\n")
f.close()
for i in 0...m.length
groups = m[i]
declaration = groups[0]
className = groups[1]
ivars = groups[2]
# Skip @accessors parsing for private classes
next if className[0, 1] == "_"
# Change "implementation" to "interface", create the .h file, and write the interface
interfaceDeclaration = declaration.sub("@implementation", "@interface")
interfaceFileName = File.dirname(fileName) + "/" + className + ".h"
interfaceFile = File.new(interfaceFileName, "a")
# Everything after this is ivar processing
next unless ivars
# Change @accessors declarations to a comment, doxygen chokes on them
ivars.gsub!("@accessors", "// @accessors")
interfaceFile.write("\n#{interfaceDeclaration}\n{#{ivars}}\n@end\n")
# Skip @accessors if it's a private class
next if className[0, 1] == "_"
writeAccessors(className, ivars, sourceFile, interfaceFile)
end
sourceFile.close()
end
def writeAccessors(className, ivars, sourceFile, interfaceFile)
# See if there are any @accessors in the ivars
accessorsMatches = ivars.scan(/\s*(\w+)\s+(\w+)\s+@accessors(\(.+?\))?;/m)
next if accessorsMatches.length == 0
return if accessorsMatches.length == 0
accessorsSource = ""
accessorsInterface = ""
# Create a CPSynthesizedAccessor category for the class with synthesized
# accessor methods for each @accessors declaration.
for accessorIndex in 0...accessorsMatches.length
ivarDeclaration = accessorsMatches[accessorIndex]
attributes = ivarDeclaration[2]
next if attributes.nil?
ivarType = ivarDeclaration[0];
ivar = ivarDeclaration[1];
ivarDeclaration = accessorsMatches[accessorIndex]
attributes = ivarDeclaration[2]
next if attributes.nil?
ivarType = ivarDeclaration[0];
ivar = ivarDeclaration[1];
attributesMatch = attributes.scan(/(\bproperty\s*=\s*(\w+)|\b(readonly)\b|\bgetter\s*=\s*(\w+)|\bsetter\s*=\s*(\w+))/m)
next if attributesMatch.length == 0
attributesMatch = attributes.scan(/(\bproperty\s*=\s*(\w+)|\b(readonly)\b|\bgetter\s*=\s*(\w+)|\bsetter\s*=\s*(\w+))/m)
next if attributesMatch.length == 0
getter = nil
setter = nil
readonly = false
getter = nil
setter = nil
readonly = false
for attributeIndex in 0...attributesMatch.length
if not attributesMatch[attributeIndex][1].nil? # property
getter = attributesMatch[attributeIndex][1]
setter = readonly ? nil : "set#{getter[0,1].upcase}#{getter[1..-1]}"
elsif not attributesMatch[attributeIndex][2].nil? # readonly
readonly = true
setter = nil
elsif not attributesMatch[attributeIndex][3].nil? # getter
getter = attributesMatch[attributeIndex][3]
elsif not attributesMatch[attributeIndex][4].nil? and not readonly # setter
setter = attributesMatch[attributeIndex][4]
for attributeIndex in 0...attributesMatch.length
if not attributesMatch[attributeIndex][1].nil? # property
getter = attributesMatch[attributeIndex][1]
setter = readonly ? nil : "set#{getter[0,1].upcase}#{getter[1..-1]}"
elsif not attributesMatch[attributeIndex][2].nil? # readonly
readonly = true
setter = nil
elsif not attributesMatch[attributeIndex][3].nil? # getter
getter = attributesMatch[attributeIndex][3]
elsif not attributesMatch[attributeIndex][4].nil? and not readonly # setter
setter = attributesMatch[attributeIndex][4]
end
end
end
# Check for @accessors with no attributes
if getter.nil? and setter.nil?
getter = ivar
setter = "set#{getter[0,1].upcase}#{getter[1..-1]}"
end
# Check for @accessors with no attributes
if getter.nil? and setter.nil?
getter = ivar
setter = "set#{getter[0,1].upcase}#{getter[1..-1]}"
end
accessorsSource += makeAccessors(getter, setter, ivar, ivarType)
accessorsSource += makeAccessors(getter, setter, ivar, ivarType, false)
accessorsInterface += makeAccessors(getter, setter, ivar, ivarType, true)
end
sourceFile.write(eval('"' + ACCESSORS_IMPLEMENTATION_TEMPLATE + '"'))
end
sourceFile.close()
end
def makeAccessors(getter, setter, ivar, ivarType)
accessors = ""
def makeAccessors(getter, setter, ivar, ivarType, isInterface)
accessors = ""
getTemplate = isInterface ? ACCESSOR_GET_TEMPLATE : ACCESSOR_GET_INTERFACE_TEMPLATE
setTemplate = isInterface ? ACCESSOR_SET_TEMPLATE : ACCESSOR_SET_INTERFACE_TEMPLATE
if not getter.nil?
accessors += eval('"' + ACCESSOR_GET_TEMPLATE + '"')
end
if not getter.nil?
accessors += eval('"' + getTemplate + '"')
end
if not setter.nil?
accessors += eval('"' + ACCESSOR_SET_TEMPLATE + '"')
end
if not setter.nil?
accessors += eval('"' + setTemplate + '"')
end
return accessors
return accessors
end
print "\033[36mGenerating header files...\033[0m\n"
fileList = Dir['AppKit.doc/**/*.j'] + Dir['Foundation.doc/**/*.j']
for fileName in fileList
makeHeaderFileFrom(fileName)
makeHeaderFileFrom(fileName)
end
@@ -18,7 +18,9 @@ transforms = [
re.compile(r"<code> \[implementation\]</code>"), "&emsp;",
re.compile(r"Static Public Member Functions"), "Class Methods",
re.compile(r"Public Member Functions"), "Instance Methods",
re.compile(r"Protected Attributes"), "Instance Variables",
re.compile(r"Member Function Documentation"), "Method Documentation",
re.compile(r"Member Data Documentation"), "Instance Variable Documentation",
re.compile(r"(AppKit|Foundation)\.doc"), r"\1",
re.compile(r"\s*<tr>\n(\s*<td></td>\n){2}\s*(<td></td>){2}<td>&emsp;</td>\n\s*</tr>"), ""
]