#!/usr/bin/env python ################################################################################ # # qooxdoo - the new era of web development # # http://qooxdoo.org # # Copyright: # 2006-2008 1&1 Internet AG, Germany, http://www.1und1.de # # License: # LGPL: http://www.gnu.org/licenses/lgpl.html # EPL: http://www.eclipse.org/org/documents/epl-v10.php # See the LICENSE file in the project's top-level directory for details. # # Authors: # * Sebastian Werner (wpbasti) # * Andreas Ecker (ecker) # * Fabian Jakobs (fjakobs) # ################################################################################ ## #
# NAME # module.py -- module short description # # SYNTAX # module.py --help # # or # # import module # result = module.func() # # DESCRIPTION # The module module does blah. # # CAVEATS # # KNOWN ISSUES # There are no known issues. ### import sys, os, re, optparse import tree, treegenerator, tokenizer, comment from treeutil import * ######################################################################################## # # MAIN # ######################################################################################## ## # Some nice short description of class Foo # # @param Super The super-class of Foo class DocException (Exception): def __init__ (self, msg, syntaxItem): Exception.__init__(self, msg) self.node = syntaxItem hasDocError = False def printDocError(node, msg): (line, column) = getLineAndColumnFromSyntaxItem(node) file = getFileFromSyntaxItem(node) print if line != None or file != None: print " - Failed: %s\n %s, Line: %s, Column: %s" % ( msg, str(file), str(line), str(column) ) else: print " - Failed: %s" % msg global hasDocError hasDocError = True def findQxDefine(rootNode): if rootNode.type == "variable": try: variableName = (assembleVariable(rootNode))[0] except tree.NodeAccessException: return None if variableName in ["qx.Class.define", "qx.Interface.define", "qx.Mixin.define"]: if rootNode.parent.parent.type == "call" and rootNode.parent.type == "operand": return rootNode.parent.parent if rootNode.hasChildren(): for child in rootNode.children: foundNode = findQxDefine(child) if foundNode is not None: return foundNode else: return None def createDoc(syntaxTree, docTree = None): if not docTree: docTree = tree.Node("doctree") file = syntaxTree.get("file") if file.endswith("__init__"): return (createPackageDoc(syntaxTree, docTree, file[:-9]), False) defineNode = findQxDefine(syntaxTree) if defineNode != None: variant = selectNode(defineNode, "operand/variable/2/@name").lower() handleClassDefinition(docTree, defineNode, variant) else: # try old style class definition of no new style class could be found docTree = createDocOld(syntaxTree, docTree) global hasDocError return (docTree, hasDocError) ######################################################################################## # # COMPATIBLE TO 0.7 STYLE ONLY! # ######################################################################################## def createPackageDoc(syntaxTree, docTree, packageName): package = getPackageNode(docTree, packageName) commentAttributes = comment.parseNode(syntaxTree.children[0]) # Read all description, param and return attributes for attrib in commentAttributes: # Add description if attrib["category"] == "description": if attrib.has_key("text"): descNode = tree.Node("desc").set("text", attrib["text"]) package.addChild(descNode) elif attrib["category"] == "see": if not attrib.has_key("name"): printDocError(classNode, "Missing target for see.") return docTree seeNode = tree.Node("see").set("name", attrib["name"]) package.addChild(seeNode) return docTree def handleClassDefinition(docTree, item, variant): params = item.getChild("params") className = params.children[0].get("value") if len(params.children) > 1: classMap = params.children[1] else: classMap = {} commentAttributes = comment.parseNode(item) classNode = getClassNode(docTree, className, commentAttributes) if variant in ["class", "clazz"]: classNode.set("type", "class") type = selectNode(params, "2/keyvalue[@key='type']/value/constant/@value") if type == "singleton": classNode.set("isSingleton", True) elif type == "abstract": classNode.set("isAbstract", True) else: classNode.set("type", variant) handleDeprecated(classNode, commentAttributes) handleAccess(classNode, commentAttributes) handleAppearance(item, classNode, className, commentAttributes) try: children = classMap.children except AttributeError: return for keyvalueItem in children: if keyvalueItem.type != "keyvalue": continue key = keyvalueItem.get("key") valueItem = keyvalueItem.getChild("value").getFirstChild() # print "KEY: %s = %s" % (key, valueItem.type) if key == "extend": if variant in ["class", "clazz"]: handleClassExtend(valueItem, classNode, docTree, className) elif variant == "interface": handleInterfaceExtend(valueItem, classNode, docTree, className) elif key == "include": handleMixins(valueItem, classNode, docTree, className) elif key == "implement": handleInterfaces(valueItem, classNode, docTree) elif key == "construct": handleConstructor(valueItem, classNode) elif key == "statics": handleStatics(valueItem, classNode) elif key == "properties": handleProperties(valueItem, classNode) elif key == "members": handleMembers(valueItem, classNode) elif key == "events": handleEvents(valueItem, classNode) handleSingleton(classNode, docTree) def handleClassExtend(valueItem, classNode, docTree, className): superClassName = (assembleVariable(valueItem))[0] if superClassName not in [ "Array", "Boolean", "Date", "Error", "Function", "Math", "Number", "Object", "RegExp", "String" ]: superClassNode = getClassNode(docTree, superClassName) childClasses = superClassNode.get("childClasses", False) if childClasses: childClasses += "," + className else: childClasses = className superClassNode.set("childClasses", childClasses) classNode.set("superClass", superClassName) def handleInterfaceExtend(valueItem, classNode, docTree, className): superInterfaceNames = variableOrArrayNodeToArray(valueItem) for superInterface in superInterfaceNames: superInterfaceNode = getClassNode(docTree, superInterface) childInterfaces = superInterfaceNode.get("interfaces", False) if childInterfaces: childInterfaces += "," + className else: childInterfaces = className superInterfaceNode.set("childClasses", childInterfaces) node = tree.Node("interface"); node.set("name", superInterface) classNode.addListChild("superInterfaces", node) def handleMixins(item, classNode, docTree, className): if classNode.get("type", False) == "mixin": superMixinNames = variableOrArrayNodeToArray(item) for superMixin in superMixinNames: superMixinNode = getClassNode(docTree, superMixin) childMixins = superMixinNode.get("mixins", False) if childMixins: childMixins += "," + className else: childMixins = className superMixinNode.set("childClasses", childMixins) node = tree.Node("interface"); node.set("name", superMixin) classNode.addListChild("superMixins", node) else: mixins = variableOrArrayNodeToArray(item) for mixin in mixins: mixinNode = getClassNode(docTree, mixin) includer = mixinNode.get("includer", False) if includer: includer += "," + className else: includer = className mixinNode.set("includer", includer) classNode.set("mixins", ",".join(mixins)) def handleSingleton(classNode, docTree): if classNode.get("isSingleton", False) == True: className = classNode.get("fullName") functionCode = """/** * Returns a singleton instance of this class. On the first call the class * is instantiated by calling the constructor with no arguments. All following * calls will return this instance. * * This method has been added by setting the "type" key in the class definition * ({@link qx.Class#define}) to "singleton". * * @type static * @return {%s} The singleton instance of this class. */ function() {}""" % className node = compileString(functionCode) commentAttributes = comment.parseNode(node) docNode = handleFunction(node, "getInstance", commentAttributes, classNode) docNode.set("isStatic", True) classNode.addListChild("methods-static", docNode) def handleInterfaces(item, classNode, docTree): className = classNode.get("fullName") interfaces = variableOrArrayNodeToArray(item) for interface in interfaces: interfaceNode = getClassNode(docTree, interface) impl = interfaceNode.get("implementations", False) if impl: impl += "," + className else: impl = className interfaceNode.set("implementations", impl) classNode.set("interfaces", ",".join(interfaces)) def handleConstructor(ctorItem, classNode): if ctorItem and ctorItem.type == "function": commentAttributes = comment.parseNode(ctorItem.parent.parent) ctor = handleFunction(ctorItem, "ctor", commentAttributes, classNode) removeErrors(ctor) ctor.set("isCtor", True) classNode.addListChild("constructor", ctor) # Check for methods defined in the constructor # (for method definition style that supports real private methods) ctorBlock = ctorItem.getChild("body").getChild("block") if ctorBlock.hasChildren(): for item in ctorBlock.children: if item.type == "assignment": leftItem = item.getFirstListChild("left") rightItem = item.getFirstListChild("right") # It's a method definition if ( leftItem.type == "variable" and len(leftItem.children) == 2 and ( leftItem.children[0].get("name") == "this" or leftItem.children[0].get("name") == "self" ) and rightItem.type == "function" ): handleMethodDefinitionOld(item, False, classNode) def handleStatics(item, classNode): for key, value in mapNodeToMap(item).items(): keyvalue = value.parent value = value.getFirstChild() commentAttributes = comment.parseNode(keyvalue) # handle @signature if value.type != "function": for docItem in commentAttributes: if docItem["category"] == "signature": value = compileString(docItem["text"][3:-4] + "{}") # Function if value.type == "function": node = handleFunction(value, key, commentAttributes, classNode) node.set("isStatic", True) if classNode.get("type", False) == "mixin": node.set("isMixin", True) classNode.addListChild("methods-static", node) # Constant elif key.isupper(): handleConstantDefinition(keyvalue, classNode) def handleMembers(item, classNode): for key, value in mapNodeToMap(item).items(): keyvalue = value.parent value = value.getFirstChild() commentAttributes = comment.parseNode(keyvalue) # handle @signature if value.type != "function": for docItem in commentAttributes: if docItem["category"] == "signature": value = compileString(docItem["text"][3:-4] + "{}") if value.type == "function": node = handleFunction(value, key, commentAttributes, classNode) if classNode.get("type", False) == "mixin": node.set("isMixin", True) classNode.addListChild("methods", node) def generatePropertyMethods(propertyName, classNode, generatedMethods): if propertyName[:2] == "__": access = "__" name = propertyName[2:] elif propertyName[:1] == "_": access = "_" name = propertyName[1:] else: access = "" name = propertyName name = name[0].upper() + name[1:] propData = { access + "set" + name : """/** * Sets the user value of the property
%s
.
*
* For further details take a look at the property definition: {@link #%s}.
*
* @param value {var} New value for property %s
.
* @return {var} The unmodified incoming value.
*/
function (value) {}; """ % (propertyName, propertyName, propertyName),
access + "get" + name : """/**
* Returns the (computed) value of the property %s
.
*
* For further details take a look at the property definition: {@link #%s}.
*
* @return {var} (Computed) value of %s
.
*/
function () {}; """ % (propertyName, propertyName, propertyName),
access + "reset" + name : """/**
* Resets the user value of the property %s
.
*
* The computed value falls back to the next available value e.g. appearance, init or
* inheritance value depeneding on the property configuration and value availability.
*
* For further details take a look at the property definition: {@link #%s}.
*
* @return {void}
*/
function () {}; """ % (propertyName, propertyName),
access + "init" + name : """/**
* Calls the apply method and dispatches the change event of the property %s
* with the default value defined by the class developer. This function can
* only be called from the constructor of a class.
*
* For further details take a look at the property definition: {@link #%s}.
*
* @protected
* @param value {var} Initial value for property %s
.
* @return {var} the default value
*/
function (value) {}; """ % (propertyName, propertyName, propertyName),
access + "toggle" + name : """/**
* Toggles the (computed) value of the boolean property %s
.
*
* For further details take a look at the property definition: {@link #%s}.
*
* @return {Boolean} the new value
*/
function () {}; """ % (propertyName, propertyName),
access + "is" + name : """/**
* Check whether the (computed) value of the boolean property %s
equals true
.
*
* For further details take a look at the property definition: {@link #%s}.
*
* @return {Boolean} Whether the property equals true
.
*/
function () {}; """ % (propertyName, propertyName)
}
for funcName in generatedMethods:
funcName = access + funcName + name
functionCode = propData[funcName]
node = compileString(functionCode)
commentAttributes = comment.parseNode(node)
docNode = handleFunction(node, funcName, commentAttributes, classNode)
docNode.set("fromProperty", propertyName)
classNode.addListChild("methods", docNode)
def handlePropertyDefinitionNew(propName, propDefinition, classNode):
node = tree.Node("property")
node.set("name", propName)
if propDefinition.has_key("init"):
node.set("defaultValue", getValue(propDefinition["init"].getFirstChild()))
if propDefinition.has_key("nullable"):
node.set("allowNull", propDefinition["nullable"].getChild("constant").get("value"))
if propDefinition.has_key("inheritable"):
node.set("inheritable", propDefinition["inheritable"].getChild("constant").get("value"))
if propDefinition.has_key("themeable"):
node.set("themeable", propDefinition["themeable"].getChild("constant").get("value"))
if propDefinition.has_key("refine"):
refineValue = propDefinition["refine"].getChild("constant").get("value")
if refineValue == "true":
node.set("refine", "true")
if propDefinition.has_key("apply"):
node.set("apply", propDefinition["apply"].getChild("constant").get("value"))
if propDefinition.has_key("event"):
eventName = propDefinition["event"].getChild("constant").get("value")
node.set("event", eventName)
event = tree.Node("event")
event.set("name", eventName)
event.addChild(tree.Node("desc").set("text", "Fired on change of the property {@link #%s}." % propName))
typesNode = tree.Node("types")
event.addChild(typesNode)
itemNode = tree.Node("entry")
typesNode.addChild(itemNode)
itemNode.set("type", "qx.event.type.ChangeEvent")
classNode.addListChild("events", event)
#checkBasic = None
if propDefinition.has_key("check"):
check = propDefinition["check"].getFirstChild()
if check.type == "array":
values = [getValue(arrayItem) for arrayItem in check.children]
node.set("possibleValues", ",".join(values))
elif check.type == "function":
node.set("check", "Custom check function.")
elif check.type == "constant":
node.set("check", check.get("value"))
#checkBasic = check.get("value")
else:
printDocError(check, "Unknown check value", check)
return node
return node
def generateGroupPropertyMethod(propertyName, groupMembers, mode, classNode):
if propertyName[:2] == "__":
access = "__"
functionName = propertyName[2:]
elif propertyName[:1] == "_":
access = "_"
functionName = propertyName[1:]
else:
access = ""
functionName = propertyName
functionName = access + "set" + functionName[0].upper() + functionName[1:]
functionTemplate = """/**
* Sets the values of the property group %(name)s
.
* %(modeDoc)s
* For further details take a look at the property definition: {@link #%(name)s}.
*
%(params)s
*/
function (%(paramList)s) {}; """
paramsTemplate = " * @param %s {var} Sets the value of the property {@link #%s}."
paramsDef = [paramsTemplate % (name, name) for name in groupMembers]
if mode == "shorthand":
modeDoc = "\n * This setter supports a shorthand mode compatible with the way margins and paddins are set in CSS.\n *"
else:
modeDoc = ""
functionCode = functionTemplate % ({
"name" : propertyName,
"modeDoc" : modeDoc,
"params" : "\n".join(paramsDef),
"paramList" : ", ".join(groupMembers)
})
functionNode = compileString(functionCode)
commentAttributes = comment.parseNode(functionNode)
docNode = handleFunction(functionNode, functionName, commentAttributes, classNode)
docNode.set("fromProperty", propertyName)
classNode.addListChild("methods", docNode)
def handlePropertyGroup(propName, propDefinition, classNode):
node = tree.Node("property")
node.set("name", propName)
group = propDefinition["group"].getFirstChild()
groupMembers = [getValue(arrayItem) for arrayItem in group.children]
node.set("group", ",".join(groupMembers));
if propDefinition.has_key("mode"):
node.set("mode", propDefinition["mode"].getChild("constant").get("value"))
if propDefinition.has_key("themeable"):
node.set("themeable", propDefinition["themeable"].getChild("constant").get("value"))
return node
def handleProperties(item, classNode):
for propName, value in mapNodeToMap(item).items():
keyvalue = value.parent
value = value.getFirstChild()
if value.type != "map":
continue
propDefinition = mapNodeToMap(value)
#print propName, propDefinition
# handle old style properties
if propDefinition.has_key("_legacy") or propDefinition.has_key("_cached"):
node = handlePropertyDefinitionOldCommon(keyvalue, classNode, propName, value)
if propDefinition.has_key("_legacy"):
node.set("propertyType", "legacy")
else:
node.set("propertyType", "cached")
continue
if propDefinition.has_key("_fast"):
node = handlePropertyDefinitionOldCommon(keyvalue, classNode, propName, value)
generatePropertyMethods(propName, classNode, ["get"])
node.set("propertyType", "fast")
continue
if propDefinition.has_key("group"):
node = handlePropertyGroup(propName, propDefinition, classNode)
node.set("propertyType", "group")
groupMembers = [member[1:-1] for member in node.get("group").split(",")]
generateGroupPropertyMethod(propName, groupMembers, node.get("mode", False), classNode)
generatePropertyMethods(propName, classNode, ["reset"])
else:
node = handlePropertyDefinitionNew(propName, propDefinition, classNode)
node.set("propertyType", "new")
if node.get("refine", False) != "true":
generatePropertyMethods(propName, classNode, ["set", "get", "init", "reset"])
if node.get("check", False) == "Boolean":
generatePropertyMethods(propName, classNode, ["toggle", "is"])
if classNode.get("type", False) == "mixin":
node.set("isMixin", True)
# If the description has a type specified then take this type
# (and not the one extracted from the paramsMap)
commentAttributes = comment.parseNode(keyvalue)
addTypeInfo(node, comment.getAttrib(commentAttributes, "description"), item)
handleDeprecated(node, commentAttributes)
handleAccess(node, commentAttributes)
classNode.addListChild("properties", node)
def handleEvents(item, classNode):
for key, value in mapNodeToMap(item).items():
keyvalue = value.parent
value = value.getFirstChild(True, True).get("value")
node = tree.Node("event")
commentAttributes = comment.parseNode(keyvalue)
try:
desc = commentAttributes[0]["text"]
except (IndexError, KeyError):
desc = None
addError(node, "Documentation is missing.", item)
if desc != None:
node.addChild(tree.Node("desc").set("text", desc))
node.set("name", key)
typesNode = tree.Node("types")
node.addChild(typesNode)
itemNode = tree.Node("entry")
typesNode.addChild(itemNode)
itemNode.set("type", value)
handleDeprecated(node, commentAttributes)
handleAccess(node, commentAttributes)
classNode.addListChild("events", node)
def handleAppearance(item, classNode, className, commentAttributes):
"""
handles the declaration of appearances and widget states
by evaluating the @state and @apprearance attributes
"""
appearances = {}
thisAppearance = []
classAppearance = None
# parse appearances
for attrib in commentAttributes:
if attrib["category"] == "appearance":
appearanceName = attrib["name"]
appearances[appearanceName] = attrib
if not attrib.has_key("type"):
attrib["type"] = className
else:
attrib["type"] = attrib["type"][0]["type"]
if attrib["type"] == className:
thisAppearance.append(appearanceName)
attrib["states"] = []
if len(thisAppearance) > 1:
printDocError(item, "The class '%s' has more than one own appearance!" % className)
return
# parse states
for attrib in commentAttributes:
if attrib["category"] == "state":
if not attrib.has_key("type"):
if thisAppearance == []:
printDocError(item,
"The default state '%s' of the class '%s' is defined but no default appearance is defined"
% (attrib["name"], className)
)
return
type = thisAppearance[0]
else:
type = attrib["type"][0]["type"]
appearances[type]["states"].append(attrib)
#generate the doc tree nodes
if len(appearances) > 0:
for name, appearance in appearances.iteritems():
appearanceNode = tree.Node("appearance")
appearanceNode.set("name", name)
appearanceNode.set("type", appearance["type"])
if appearance.has_key("text"):
appearanceNode.addChild(tree.Node("desc").set("text", appearance["text"]))
for state in appearance["states"]:
stateNode = tree.Node("state")
stateNode.set("name", state["name"])
if state.has_key("text"):
stateNode.addChild(tree.Node("desc").set("text", state["text"]))
appearanceNode.addListChild("states", stateNode)
classNode.addListChild("appearances", appearanceNode)
def handleDeprecated(docNode, commentAttributes):
for docItem in commentAttributes:
if docItem["category"] == "deprecated":
deprecatedNode = tree.Node("deprecated")
if docItem.has_key("text"):
descNode = tree.Node("desc").set("text", docItem["text"])
deprecatedNode.addChild(descNode)
docNode.addChild(deprecatedNode)
def handleAccess(docNode, commentAttributes):
name = docNode.get("name")
if name[:2] == "__":
access = "private"
elif name[:1] == "_":
access = "protected"
else:
access = "public"
for docItem in commentAttributes:
if docItem["category"] == "internal":
access = "internal"
docNode.set("isInternal", True)
elif docItem["category"] == "public":
access = "public"
elif docItem["category"] == "protected":
access = "protected"
elif docItem["category"] == "public":
access = "public"
if access != "public":
docNode.set("access", access)
########################################################################################
#
# COMPATIBLE TO 0.6 STYLE ONLY!
#
########################################################################################
def createDocOld(syntaxTree, docTree = None):
currClassNode = None
if not syntaxTree.hasChildren():
return docTree
for item in syntaxTree.children:
if item.type == "assignment":
leftItem = item.getFirstListChild("left")
rightItem = item.getFirstListChild("right")
if leftItem.type == "variable":
if (
currClassNode and len(leftItem.children) == 3 and
leftItem.children[0].get("name") == "qx"
):
if (
leftItem.children[1].get("name") == "Proto" and
rightItem.type == "function"
):
# It's a method definition
handleMethodDefinitionOld(item, False, currClassNode)
elif leftItem.children[1].get("name") == "Clazz":
if rightItem.type == "function":
handleMethodDefinitionOld(item, True, currClassNode)
elif leftItem.children[2].get("name").isupper():
handleConstantDefinition(item, currClassNode)
elif (
currClassNode and
(assembleVariable(leftItem))[0].startswith(currClassNode.get("fullName"))
):
# This is definition of the type "mypackage.MyClass.bla = ..."
if rightItem.type == "function":
handleMethodDefinitionOld(item, True, currClassNode)
elif leftItem.children[len(leftItem.children) - 1].get("name").isupper():
handleConstantDefinition(item, currClassNode)
elif item.type == "call":
operand = item.getChild("operand", False)
if operand:
var = operand.getChild("variable", False)
# qooxdoo < 0.7 (DEPRECATED)
if var and len(var.children) == 3 and var.children[0].get("name") == "qx" and var.children[1].get("name") == "OO":
methodName = var.children[2].get("name")
if methodName == "defineClass":
currClassNode = handleClassDefinitionOld(docTree, item)
elif methodName in ["addProperty", "addFastProperty"]:
# these are private and should be marked if listed, otherwise just hide them (wpbasti)
#or methodName == "addCachedProperty" or methodName == "changeProperty":
handlePropertyDefinitionOld(item, currClassNode)
return docTree
def handleClassDefinitionOld(docTree, item):
params = item.getChild("params")
paramsLen = len(params.children);
if paramsLen == 1:
superClassName = "Object"
ctorItem = None
elif paramsLen == 2:
superClassName = "Object"
ctorItem = params.children[1]
elif paramsLen == 3:
superClassName = (assembleVariable(params.children[1]))[0]
ctorItem = params.children[2]
else:
printDocError(item, "defineClass call has more than three parameters: " + str(len(params.children)))
return classNode
className = params.children[0].get("value")
classNode = getClassNode(docTree, className)
if superClassName != "Object":
superClassNode = getClassNode(docTree, superClassName)
childClasses = superClassNode.get("childClasses", False)
if childClasses:
childClasses += "," + className
else:
childClasses = className
superClassNode.set("childClasses", childClasses)
classNode.set("superClass", superClassName)
commentAttributes = comment.parseNode(item)
for attrib in commentAttributes:
if attrib["category"] == "event":
# Add the event
if comment.attribHas(attrib, "name") and comment.attribHas(attrib, "type"):
addEventNode(classNode, item, attrib);
else:
addError(classNode, "Documentation contains malformed event attribute.", item)
elif attrib["category"] == "description":
if attrib.has_key("text"):
descNode = tree.Node("desc").set("text", attrib["text"])
classNode.addChild(descNode)
# Add the constructor
if ctorItem and ctorItem.type == "function":
ctor = handleFunction(ctorItem, "ctor", commentAttributes, classNode)
ctor.set("isCtor", True)
classNode.addListChild("constructor", ctor)
# Check for methods defined in the constructor
# (for method definition style that supports real private methods)
ctorBlock = ctorItem.getChild("body").getChild("block")
if ctorBlock.hasChildren():
for item in ctorBlock.children:
if item.type == "assignment":
leftItem = item.getFirstListChild("left")
rightItem = item.getFirstListChild("right")
# It's a method definition
if (
leftItem.type == "variable" and
len(leftItem.children) == 2 and
(
leftItem.children[0].get("name") == "this" or
leftItem.children[0].get("name") == "self"
) and
rightItem.type == "function"
):
handleMethodDefinitionOld(item, False, classNode)
elif ctorItem and ctorItem.type == "map":
for keyvalueItem in ctorItem.children:
if keyvalueItem.type == "keyvalue":
valueItem = keyvalueItem.getChild("value").getFirstChild()
if (valueItem.type == "function"):
handleMethodDefinitionOld(keyvalueItem, True, classNode)
else:
handleConstantDefinition(keyvalueItem, classNode)
return classNode
def handlePropertyDefinitionOld(item, classNode):
paramsMap = item.getChild("params").getChild("map")
propertyName = paramsMap.getChildByAttribute("key", "name").getChild("value").getChild("constant").get("value")
return handlePropertyDefinitionOldCommon(item, classNode, propertyName, paramsMap)
def handlePropertyDefinitionOldCommon(item, classNode, propertyName, paramsMap):
node = tree.Node("property")
node.set("name", propertyName)
node.set("oldProperty", True)
propType = paramsMap.getChildByAttribute("key", "type", False)
if propType:
node.set("type", getType(propType.getChild("value").getFirstChild()))
allowNull = paramsMap.getChildByAttribute("key", "allowNull", False)
if allowNull:
node.set("allowNull", allowNull.getChild("value").getChild("constant").get("value"))
defaultValue = paramsMap.getChildByAttribute("key", "defaultValue", False)
if defaultValue:
node.set("defaultValue", getValue(defaultValue.getFirstListChild("value")))
getAlias = paramsMap.getChildByAttribute("key", "getAlias", False)
if getAlias:
node.set("getAlias", getAlias.getChild("value").getChild("constant").get("value"))
setAlias = paramsMap.getChildByAttribute("key", "setAlias", False)
if setAlias:
node.set("setAlias", setAlias.getChild("value").getChild("constant").get("value"))
unitDetection = paramsMap.getChildByAttribute("key", "unitDetection", False)
if unitDetection:
node.set("unitDetection", unitDetection.getChild("value").getChild("constant").get("value"))
instance = paramsMap.getChildByAttribute("key", "instance", False)
if instance:
node.set("instance", instance.getChild("value").getChild("constant").get("value"))
classname = paramsMap.getChildByAttribute("key", "classname", False)
if classname:
node.set("classname", classname.getChild("value").getChild("constant").get("value"))
possibleValues = paramsMap.getChildByAttribute("key", "possibleValues", False)
if possibleValues:
array = possibleValues.getChild("value").getChild("array")
values = ""
for arrayItem in array.children:
if len(values) != 0:
values += ", "
values += getValue(arrayItem)
node.set("possibleValues", values)
if classNode.get("type", False) == "mixin":
node.set("isMixin", True)
# If the description has a type specified then take this type
# (and not the one extracted from the paramsMap)
commentAttributes = comment.parseNode(item)
addTypeInfo(node, comment.getAttrib(commentAttributes, "description"), item)
handleDeprecated(node, commentAttributes)
handleAccess(node, commentAttributes)
classNode.addListChild("properties", node)
return node
def handleMethodDefinitionOld(item, isStatic, classNode):
if item.type == "assignment":
# This is a "normal" method definition
leftItem = item.getFirstListChild("left")
name = leftItem.children[len(leftItem.children) - 1].get("name")
functionItem = item.getFirstListChild("right")
elif item.type == "keyvalue":
# This is a method definition of a map-style class (like qx.Const)
name = item.get("key")
functionItem = item.getFirstListChild("value")
commentAttributes = comment.parseNode(item)
node = handleFunction(functionItem, name, commentAttributes, classNode)
listName = "methods"
if isStatic:
node.set("isStatic", True)
listName += "-static"
classNode.addListChild(listName, node)
return node
########################################################################################
#
# COMPATIBLE TO BOTH, 0.6 and 0.7 style
#
########################################################################################
def handleConstantDefinition(item, classNode):
if (item.type == "assignment"):
# This is a "normal" constant definition
leftItem = item.getFirstListChild("left")
name = leftItem.children[len(leftItem.children) - 1].get("name")
valueNode = item.getChild("right")
elif (item.type == "keyvalue"):
# This is a constant definition of a map-style class (like qx.Const)
name = item.get("key")
valueNode = item.getChild("value")
if not name.isupper():
return
node = tree.Node("constant")
node.set("name", name)
value = None
if valueNode.hasChild("constant"):
node.set("value", valueNode.getChild("constant").get("value"))
node.set("type", valueNode.getChild("constant").get("constantType").capitalize())
commentAttributes = comment.parseNode(item)
description = comment.getAttrib(commentAttributes, "description")
addTypeInfo(node, description, item)
handleDeprecated(node, commentAttributes)
handleAccess(node, commentAttributes)
classNode.addListChild("constants", node)
def handleFunction(funcItem, name, commentAttributes, classNode):
node = tree.Node("method")
node.set("name", name)
if funcItem.type != "function":
printDocError(funcItem, "'funcItem' is no function")
return node
# Read the parameters
params = funcItem.getChild("params", False)
if params and params.hasChildren():
for param in params.children:
if param.type != "variable":
continue
paramNode = tree.Node("param")
paramNode.set("name", param.getFirstChild().get("name"))
node.addListChild("params", paramNode)
# Check whether the function is abstract
bodyBlockItem = funcItem.getChild("body").getFirstChild();
if bodyBlockItem.type == "block" and bodyBlockItem.hasChildren():
firstStatement = bodyBlockItem.children[0];
handleAccess(node, commentAttributes)
if len(commentAttributes) == 0:
addError(node, "Documentation is missing.", funcItem)
return node
handleDeprecated(node, commentAttributes)
# Read all description, param and return attributes
for attrib in commentAttributes:
# Add description
if attrib["category"] == "description":
if attrib.has_key("text"):
if "TODOC" in attrib["text"]:
addError(node, "Documentation is missing.", funcItem)
descNode = tree.Node("desc").set("text", attrib["text"])
node.addChild(descNode)
elif attrib["category"] == "see":
if not attrib.has_key("name"):
printDocError(funcItem, "Missing target for see.")
return node
seeNode = tree.Node("see").set("name", attrib["name"])
node.addChild(seeNode)
elif attrib["category"] == "param":
if not attrib.has_key("name"):
printDocError(funcItem, "Missing name of parameter.")
return node
# Find the matching param node
paramName = attrib["name"]
paramNode = node.getListChildByAttribute("params", "name", paramName, False)
if not paramNode:
addError(node, "Contains information for a non-existing parameter %s
." % paramName, funcItem)
continue
addTypeInfo(paramNode, attrib, funcItem)
elif attrib["category"] == "return":
returnNode = tree.Node("return")
node.addChild(returnNode)
addTypeInfo(returnNode, attrib, funcItem)
# Check for documentation errors
# Check whether all parameters have been documented
if node.hasChild("params"):
paramsListNode = node.getChild("params");
for paramNode in paramsListNode.children:
if not paramNode.getChild("desc", False):
addError(node, "Parameter %s
is not documented." % paramNode.get("name"), funcItem)
if not node.hasChild("desc"):
addError(node, "Documentation is missing.", funcItem)
return node
########################################################################################
#
# COMMON STUFF
#
#######################################################################################
def variableIsClassName(varItem):
length = len(varItem.children)
for i in range(length):
varChild = varItem.children[i]
if not varChild.type == "identifier":
return False
if i < length - 1:
# This is not the last identifier -> It must a package (= lowercase)
if not varChild.get("name").islower():
return False
else:
# This is the last identifier -> It must the class name (= first letter uppercase)
if not varChild.get("name")[0].isupper():
return False
return True
def getValue(item):
value = None
if item.type == "constant":
if item.get("constantType") == "string":
value = '"' + item.get("value") + '"'
else:
value = item.get("value")
elif item.type == "variable":
value, isComplete = assembleVariable(item)
if not isComplete:
value = "[Complex expression]"
elif item.type == "operation" and item.get("operator") == "SUB":
# E.g. "-1" or "-Infinity"
value = "-" + getValue(item.getChild("first").getFirstChild())
if value == None:
value = "[Unsupported item type: " + item.type + "]"
return value
def addTypeInfo(node, commentAttrib=None, item=None):
if commentAttrib == None:
if node.type == "constant" and node.get("value", False):
pass
elif node.type == "param":
addError(node, "Parameter %s
in not documented." % commentAttrib.get("name"), item)
elif node.type == "return":
addError(node, "Return value is not documented.", item)
else:
addError(node, "Documentation is missing.", item)
return
# add description
if commentAttrib.has_key("text"):
node.addChild(tree.Node("desc").set("text", commentAttrib["text"]))
# add types
if commentAttrib.has_key("type"):
typesNode = tree.Node("types")
node.addChild(typesNode)
for item in commentAttrib["type"]:
itemNode = tree.Node("entry")
typesNode.addChild(itemNode)
itemNode.set("type", item["type"])
if item["dimensions"] != 0:
itemNode.set("dimensions", item["dimensions"])
# add default value
if commentAttrib.has_key("defaultValue"):
defaultValue = commentAttrib["defaultValue"]
if defaultValue != None:
# print "defaultValue: %s" % defaultValue
node.set("defaultValue", defaultValue)
def addEventNode(classNode, classItem, commentAttrib):
node = tree.Node("event")
node.set("name", commentAttrib["name"])
if commentAttrib.has_key("text"):
node.addChild(tree.Node("desc").set("text", commentAttrib["text"]))
# add types
if commentAttrib.has_key("type"):
typesNode = tree.Node("types")
node.addChild(typesNode)
for item in commentAttrib["type"]:
itemNode = tree.Node("entry")
typesNode.addChild(itemNode)
itemNode.set("type", item["type"])
if item["dimensions"] != 0:
itemNode.set("dimensions", item["dimensions"])
classNode.addListChild("events", node)
def addError(node, msg, syntaxItem):
# print ">>> %s" % msg
errorNode = tree.Node("error")
errorNode.set("msg", msg)
(line, column) = getLineAndColumnFromSyntaxItem(syntaxItem)
if line:
errorNode.set("line", line)
if column:
errorNode.set("column", column)
node.addListChild("errors", errorNode)
node.set("hasError", True)
def getType(item):
if item.type == "constant" and item.get("constantType") == "string":
val = item.get("value").capitalize()
return val
else:
printDocError(item, "Can't gess type. type is neither string nor variable: " + item.type)
return "unknown"
def getPackageNode(docTree, namespace):
currPackage = docTree
childPackageName = ""
for nsPart in namespace.split("."):
childPackage = currPackage.getListChildByAttribute("packages", "name", nsPart, False)
childPackageName += nsPart
if not childPackage:
# The package does not exist -> Create it
childPackage = tree.Node("package")
childPackage.set("name", nsPart)
childPackage.set("fullName", childPackageName)
childPackage.set("packageName", childPackageName.replace("." + nsPart, ""))
currPackage.addListChild("packages", childPackage)
childPackageName += "."
# Update current package
currPackage = childPackage
return currPackage
def getClassNode(docTree, fullClassName, commentAttributes = None):
if commentAttributes == None:
commentAttributes = {}
dotIndex = fullClassName.rindex(".")
packageName = fullClassName[:dotIndex]
className = fullClassName[dotIndex+1:]
package = getPackageNode(docTree, packageName)
classNode = package.getListChildByAttribute("classes", "name", className, False)
if not classNode:
# The class does not exist -> Create it
classNode = tree.Node("class")
classNode.set("name", className)
classNode.set("fullName", fullClassName)
classNode.set("packageName", fullClassName.replace("." + className, ""))
# Read all description, param and return attributes
for attrib in commentAttributes:
# Add description
if attrib["category"] == "description":
if attrib.has_key("text"):
descNode = tree.Node("desc").set("text", attrib["text"])
classNode.addChild(descNode)
elif attrib["category"] == "see":
if not attrib.has_key("name"):
printDocError(classNode, "Missing target for see.")
return classNode
seeNode = tree.Node("see").set("name", attrib["name"])
classNode.addChild(seeNode)
package.addListChild("classes", classNode)
return classNode
def postWorkPackage(docTree, packageNode):
childHasError = False
packages = packageNode.getChild("packages", False)
if packages:
packages.children.sort(nameComparator)
for node in packages.children:
hasError = postWorkPackage(docTree, node)
if hasError:
childHasError = True
classes = packageNode.getChild("classes", False)
if classes:
classes.children.sort(nameComparator)
for node in classes.children:
hasError = postWorkClass(docTree, node)
if hasError:
childHasError = True
if childHasError:
packageNode.set("hasWarning", True)
return childHasError
def postWorkClass(docTree, classNode):
# mark property apply methods
markPropertyApply(docTree, classNode)
# Sort child classes
childClasses = classNode.get("childClasses", False)
if childClasses:
classArr = childClasses.split(",")
classArr.sort()
childClasses = ",".join(classArr)
classNode.set("childClasses", childClasses)
# Mark overridden items
postWorkItemList(docTree, classNode, "properties", True)
postWorkItemList(docTree, classNode, "events", False)
postWorkItemList(docTree, classNode, "methods", True)
postWorkItemList(docTree, classNode, "methods-static", False)
# Check whether the class is static
superClassName = classNode.get("superClass", False)
if (superClassName == None) \
and classNode.getChild("properties", False) == None \
and classNode.getChild("methods", False) == None:
# This class is static
classNode.set("isStatic", True)
# Check whether the class is abstract
if isClassAbstract(docTree, classNode, {}):
classNode.set("isAbstract", True)
# Check for errors
childHasError = listHasError(classNode, "constructor") or listHasError(classNode, "properties") \
or listHasError(classNode, "methods") or listHasError(classNode, "methods-static") \
or listHasError(classNode, "constants") or listHasError(classNode, "events")
if childHasError:
classNode.set("hasWarning", True)
return childHasError
def isClassAbstract(docTree, classNode, visitedMethodNames):
if containsAbstractMethods(classNode.getChild("methods", False), visitedMethodNames):
# One of the methods is abstract
return True
# No abstract methods found -> Check whether the super class has abstract
# methods that haven't been overridden
superClassName = classNode.get("superClass", False)
if superClassName:
superClassNode = getClassNode(docTree, superClassName)
return isClassAbstract(docTree, superClassNode, visitedMethodNames)
def containsAbstractMethods(methodListNode, visitedMethodNames):
if methodListNode:
for methodNode in methodListNode.children:
name = methodNode.get("name")
if not name in visitedMethodNames:
visitedMethodNames[name] = True
if methodNode.get("isAbstract", False):
return True
return False
def documentApplyMethod(methodNode, prop):
if itemHasAnyDocs(methodNode):
return
firstParam = selectNode(methodNode, "params/param[1]/@name")
if firstParam is None:
firstParam = "value"
secondParam = selectNode(methodNode, "params/param[2]/@name")
if secondParam is None:
secondParam = "old"
paramType = prop.get("check", False)
if paramType is None or paramType == "Custom check function.":
paramType = "var"
functionCode = """/**
* Applies changes of the property value of the property %(shortPropName)s
.
*
* For further details take a look at the property definition: {@link #%(propName)s}.
*
* @param %(firstParamName)s {%(paramType)s} new value of the property
* @param %(secondParamName)s {%(paramType)s} previous value of the property (null if it was not yet set).
*/
function(%(firstParamName)s, %(secondParamName)s) {}""" % ({
"firstParamName": firstParam,
"secondParamName": secondParam,
"paramType": paramType,
"shortPropName": prop.get("name"),
"propName": methodNode.get("name")
})
node = compileString(functionCode)
commentAttributes = comment.parseNode(node)
docNode = handleFunction(node, methodNode.get("name"), commentAttributes, selectNode(methodNode, "../.."))
oldParams = methodNode.getChild("params", False)
if oldParams:
methodNode.replaceChild(oldParams, docNode.getChild("params"))
else:
methodNode.addChild(docNode.getChild("params"))
oldDesc = methodNode.getChild("desc", False)
if oldDesc:
methodNode.replaceChild(oldDesc, docNode.getChild("desc"))
else:
methodNode.addChild(docNode.getChild("desc"))
def markPropertyApply(docTree, classNode):
# Sort the list
sortByName(classNode, "methods")
# Post work all items
methods = classNode.getChild("methods", False)
if not methods:
return
dependendClasses = [cls for cls in dependendClassIterator(docTree, classNode)]
for itemNode in methods.children:
name = itemNode.get("name")
for dep in dependendClasses:
props = dep.getChild("properties", False)
if not props:
continue
for prop in props.children:
if prop.get("apply", None) == name:
itemNode.set("apply", dep.get("fullName") + "#" + prop.get("name"))
removeErrors(itemNode)
documentApplyMethod(itemNode, prop)
def dependendClassIterator(docTree, classNode):
yield classNode
directDependencies = []
superClassName = classNode.get("superClass", False)
if superClassName:
directDependencies.append(superClassName)
for list in ["mixins", "interfaces", "superMixins", "superInterfaces"]:
listItems = classNode.get(list, False)
if listItems:
directDependencies.extend(listItems.split(","))
for dep in directDependencies:
for cls in dependendClassIterator(docTree, getClassNode(docTree, dep)):
yield cls
def itemHasAnyDocs(node):
if node.getChild("desc", False) != None:
return True
if node.hasChildren():
for child in node.children:
if child.type == "params":
for param in child.children:
if param.getChild("desc", False) != None:
return True
elif child.type != "errors":
return True
return False
def postWorkItemList(docTree, classNode, listName, overridable):
"""Does the post work for a list of properties or methods."""
# Sort the list
sortByName(classNode, listName)
# Post work all items
listNode = classNode.getChild(listName, False)
if listNode:
for itemNode in listNode.children:
name = itemNode.get("name")
# Check whether this item is overridden and try to inherit the
# documentation from the next matching super class
if not overridable:
continue
superClassName = classNode.get("superClass", False)
overriddenFound = False
docFound = itemHasAnyDocs(itemNode)
# look for documentation in interfaces
if (not docFound):
for item in dependendClassIterator(docTree, classNode):
if item.get("type", False) == "interface":
interfaceItemNode = item.getListChildByAttribute(listName, "name", name, False)
if not interfaceItemNode:
continue
itemNode.set("docFrom", item.get("fullName"))
docFound = itemHasAnyDocs(interfaceItemNode)
# Remove previously recorded documentation errors from the item
# (Any documentation errors will be recorded in the super class)
removeErrors(itemNode)
break
# look for documentation in super classes
while superClassName and (not overriddenFound or not docFound):
superClassNode = getClassNode(docTree, superClassName)
superItemNode = superClassNode.getListChildByAttribute(listName, "name", name, False)
if superItemNode:
if not docFound:
# This super item has a description
# -> Check whether the parameters match
# NOTE: paramsMatch works for properties, too
# (Because both compared properties always have no params)
if paramsMatch(itemNode, superItemNode):
# The parameters match -> We can use the documentation of the super class
itemNode.set("docFrom", superClassName)
docFound = itemHasAnyDocs(superItemNode)
# Remove previously recorded documentation errors from the item
# (Any documentation errors will be recorded in the super class)
removeErrors(itemNode)
else:
docFound = True;
if not overriddenFound:
# This super class has the item defined -> Add a overridden attribute
itemNode.set("overriddenFrom", superClassName)
overriddenFound = True
# Check the next superclass
superClassName = superClassNode.get("superClass", False)
if not docFound and itemNode.get("overriddenFrom", False):
# This item is overridden, but we didn't find any documentation in the
# super classes -> Add a warning
itemNode.set("hasWarning", True)
def paramsMatch(methodNode1, methodNode2):
params1 = methodNode1.getChild("params", False)
params2 = methodNode2.getChild("params", False)
if params1 == None or params2 == None:
# One method has no parameters -> The params match if both are None
return params1 == params2
elif len(params1.children) != len(params2.children):
# The param count is different -> The params don't match
return False
else:
for i in range(len(params1.children)):
par1 = params1.children[i]
par2 = params2.children[i]
if (par1.get("name") != par2.get("name")):
# These parameters don't match
return False
# All tests passed
return True
def removeErrors(node):
errors = node.getChild("errors", False)
node.remove("hasWarning")
if errors:
node.removeChild(errors)
node.remove("hasError")
def sortByName(node, listName):
listNode = node.getChild(listName, False)
if listNode:
listNode.children.sort(nameComparator)
def nameComparator(node1, node2):
name1 = node1.get("name").lower()
name2 = node2.get("name").lower()
return cmp(name1, name2)
def listHasError(node, listName):
listNode = node.getChild(listName, False)
if listNode:
for childNode in listNode.children:
if childNode.get("hasError", False):
return True
return False
def packagesToJsonString(node, prefix = "", childPrefix = " ", newLine="\n", encoding="utf-8"):
asString = prefix + '{type:"' + tree.escapeJsonChars(node.type) + '"'
if node.type == "class":
node.set("externalRef", True)
if node.hasAttributes():
asString += ',attributes:{'
firstAttribute = True
for key in node.attributes:
if not firstAttribute:
asString += ','
asString += '"' + key + '":"' + tree.escapeJsonChars(node.attributes[key]) + '"'
firstAttribute = False
asString += '}'
if node.type == "class":
node.remove("externalRef")
if node.hasChildren() and node.type != "class":
asString += ',children:[' + newLine
firstChild = True
prefix = prefix + childPrefix
for child in node.children:
asString += packagesToJsonString(child, prefix, childPrefix, newLine) + ',' + newLine
firstChild = False
# NOTE We remove the ',\n' of the last child
if newLine == "":
asString = asString[:-1] + prefix + ']'
else:
asString = asString[:-2] + newLine + prefix + ']'
asString += '}'
return asString
def packagesToXmlString(node, prefix = "", childPrefix = " ", newLine="\n", encoding="utf-8"):
if node.type == "class":
node.set("externalRef", True)
hasText = False
asString = prefix + "<" + node.type
if node.hasAttributes():
for key in node.attributes:
if key == "text":
hasText = True
else:
asString += " " + key + "=\"" + tree.escapeXmlChars(node.attributes[key], True, encoding) + "\""
if node.type == "class":
node.remove("externalRef")
if not node.hasChildren() and not hasText:
asString += "/>" + newLine
else:
asString += ">"
if hasText:
asString += newLine + prefix + childPrefix
asString += "