1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
|
// @ExecutionModes({on_single_node="/menu_bar/help[scripting_api_generator_title]"})
// Copyright (C) 2009-2011 Dave (Dke211, initial author), Volker Boerchers (adaptation for Freeplane)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
import java.lang.reflect.Method
import org.freeplane.core.ui.components.UITools
import org.freeplane.core.util.FreeplaneVersion
import org.freeplane.core.util.HtmlUtils
import org.freeplane.core.util.LogUtils
import org.freeplane.core.util.TextUtils
import org.freeplane.plugin.script.FreeplaneScriptBaseClass
import org.freeplane.plugin.script.proxy.Convertible
import org.freeplane.plugin.script.proxy.Proxy
import org.freeplane.plugin.script.proxy.ScriptUtils;
// FIXME: api is installed locally but is there a portable way to find it?
URI getApiLink(String path) {
try {
def apiBase = path.startsWith('org/freeplane') ? 'http://freeplane.sourceforge.net/doc/api'
: 'http://groovy.codehaus.org/groovy-jdk'
return new URI(apiBase + '/' + path)
} catch (Exception e) {
logger.severe("could not create link for class ${path}", e)
return null
}
}
URI getApiLink(Class clazz) {
if (clazz == void.class)
return null
else if (clazz.isArray())
clazz = List.class // that's a useful approximation in Groovy
else if (clazz.isPrimitive())
clazz = wrap(clazz)
return getApiLink(clazz.name.replace('.', '/').replace('$', '.') + '.html')
}
// see http://stackoverflow.com/questions/1704634/simple-way-to-get-wrapper-class-type-in-java
private static <T> Class<T> wrap(Class<T> clazz) {
if (clazz == boolean.class) return Boolean.class
if (clazz == byte.class) return Byte.class
if (clazz == char.class) return Character.class
if (clazz == double.class) return Double.class
if (clazz == float.class) return Float.class
if (clazz == int.class) return Integer.class
if (clazz == long.class) return Long.class
if (clazz == short.class) return Short.class
if (clazz == void.class) return Void.class
return clazz
}
def makeApi(Proxy.Node node, Class clazz) {
def classNode = node.createChild(typeToString(clazz))
TreeMap<String, Map<String, Object>> memberMap = new TreeMap<String, Map<String, Object>>()
classNode.link.uri = getApiLink(clazz)
classNode.style.font.bold = true
clazz.getMethods().findAll {
it.declaringClass == clazz || it.declaringClass.simpleName.endsWith('RO')
}.sort {
a,b -> b.name <=> a.name
}.each {
if (!addProperty(memberMap, it))
addMethod(memberMap, it);
}
classNode.createChild('Package: ' + clazz.getPackage().name)
classNode.folded = true
memberMap.each { k,v ->
createMemberNode(k, v, classNode)
}
// input for freeplane_plugin_script/src-jsyntaxpane/META-INF/services/jsyntaxpane/syntaxkits/groovysyntaxkit/combocompletions.txt
boolean printCompletionList = false
if (printCompletionList && classNode.to.plain == 'Node')
printCompletions(memberMap)
}
def printCompletions(TreeMap<String, Map<String, Object>> memberMap) {
TreeSet completions = new TreeSet()
completions.addAll(['logger', 'ui', 'htmlUtils', 'textUtils', 'node', 'import', 'def', 'String'])
memberMap.each { memberName,attribs ->
if (attribs['method'])
completions << memberName + '(|)'
else {
completions << memberName
if (attribs['type_read'] && Collection.class.isAssignableFrom(attribs['type_read'])) {
completions << memberName + '.each{ | }'
completions << memberName + '.collect{ | }'
completions << memberName + '.sum(|){ }'
}
}
}
println completions.join("\n")
}
def createMemberNode(String memberName, Map<String, Object> attribs, Proxy.Node classNode) {
Proxy.Node memberNode
if (attribs['method']) {
memberNode = classNode.createChild(attribs['method'])
memberNode.icons.add('bookmark')
}
else {
// property
def mode = (attribs['type_read'] ? 'r' : '') + (attribs['type_write'] ? 'w' : '')
def type = attribs['type_read'] ? attribs['type_read'] : attribs['type_write']
// if (mode == 'rw' && attribs['type_read'] != attribs['type_write']) {
// logger.severe("property ${memberName} has differing getter and setter types")
// }
memberNode = classNode.createChild(formatProperty(memberName, typeToString(type), mode))
memberNode.icons.add('wizard')
[ 'method_read', 'method_write' ].each {
if (attribs[it]) {
Proxy.Node methodNode = memberNode.createChild(formatMethod(attribs[it]))
methodNode.icons.add('bookmark')
methodNode.link.uri = getApiLink(attribs['return_type'])
}
}
}
if (attribs['return_type']) {
memberNode.link.uri = getApiLink(attribs['return_type'])
}
attribs['types'].each {
def typeNode = memberNode.createChild(typeToString(it))
typeNode.link.uri = getApiLink(it)
}
memberNode.folded = true
return memberNode
}
def typeToString(Class clazz) {
return clazz.simpleName.replace('Proxy$', '')
}
// returns a value if this method is a getter or setter otherwise it returns null
def addProperty(Map<String, Map<String, Object>> memberMap, Method method) {
if (isGetter(method) && ! method.parameterTypes) {
def propertyMap = getOrCreatePropertiesMap(memberMap, getPropertyName(method))
propertyMap['read'] = true
propertyMap['type_read'] = method.returnType
propertyMap['method_read'] = method
propertyMap['return_type'] = method.returnType
}
else if (isSetter(method) && method.parameterTypes.size() == 1) {
def propertyMap = getOrCreatePropertiesMap(memberMap, getPropertyName(method))
propertyMap['write'] = true
propertyMap['type_write'] = method.parameterTypes[0]
propertyMap['method_write'] = method
propertyMap['return_type'] = method.returnType
}
}
def addMethod(Map<String, Map<String, Object>> memberMap, Method method) {
def propertyMap = getOrCreatePropertiesMap(memberMap, method.name)
propertyMap['types'] = method.parameterTypes
propertyMap['method'] = formatMethod(method)
propertyMap['return_type'] = method.returnType
}
def formatProperty(String property, String type, String mode) {
return "<html><body><b>${property}</b>: ${type} (${mode})"
// Plain text:
// return "${property}: ${type} (${mode})"
}
def formatParameter(Class clazz) {
def uri = getApiLink(clazz)
if (uri)
"<a href='${uri.toURL()}'>${typeToString(clazz)}</a>"
}
def formatMethod(Method method) {
return '<html><body>' + typeToString(method.returnType) +
' <b>' + method.name + '</b>' +
'(' + method.parameterTypes.collect{ formatParameter(it) }.join(', ') + ')'
// Plain text:
// return typeToString(method.returnType) +
// ' ' + method.name +
// '(' + method.parameterTypes.collect{ typeToString(it) }.join(', ') + ')'
}
def isGetter(Method method) {
return method.name =~ '^(?:[gs]et|is)[A-Z].*'
}
def isSetter(Method method) {
return method.name =~ '^set[A-Z].*'
}
/** returns null if this is not a proper bean method name (get/set/is). */
def getPropertyName(Method method) {
def name = method.name.replaceFirst('^(?:[gs]et|is)([A-Z])', '$1')
if (name != method.name)
return name.substring(0, 1).toLowerCase() + name.substring(1)
else
return null
}
private Map getOrCreatePropertiesMap(Map properties, String name) {
def propertyMap = properties[name]
if (propertyMap == null) {
propertyMap = [:]
properties[name] = propertyMap
}
return propertyMap
}
def initHeading(Proxy.Node node) {
node.style.font.bold = true
}
def createChild(Proxy.Node parent, text, link) {
def result = parent.createChild(text)
result.link.text = link
return result
}
// == MAIN ==
def MAP_NAME = textUtils.getText('scripting_api_generator_title')
def PROXY_NODE = textUtils.getText('scripting_api_generator_proxy')
def UTILITES_NODE = textUtils.getText('scripting_api_generator_utilities')
def WEB_NODE = textUtils.getText('scripting_api_generator_web')
def LEGEND_NODE = textUtils.getText('scripting_api_generator_legend')
c.deactivateUndo()
Proxy.Map newMap = c.newMap()
def oldName = newMap.name
newMap.name = MAP_NAME
newMap.root.text = MAP_NAME
newMap.root.style.font.bold = true
newMap.root.link.uri = getApiLink('index.html')
initHeading(newMap.root)
// Proxy
def proxy = createChild(newMap.root, PROXY_NODE, getApiLink(Proxy.class))
initHeading(proxy)
makeApi(proxy, Proxy.Attributes.class)
makeApi(proxy, Proxy.Cloud.class)
makeApi(proxy, Proxy.Connector.class)
makeApi(proxy, Proxy.Controller.class)
makeApi(proxy, Proxy.Edge.class)
makeApi(proxy, Proxy.ExternalObject.class)
makeApi(proxy, Proxy.Font.class)
makeApi(proxy, Proxy.Icons.class)
makeApi(proxy, Proxy.Link.class)
makeApi(proxy, Proxy.Map.class)
makeApi(proxy, Proxy.Node.class)
makeApi(proxy, Proxy.NodeStyle.class)
makeApi(proxy, Convertible.class)
def utils = createChild(newMap.root, UTILITES_NODE, null)
initHeading(utils)
makeApi(utils, FreeplaneScriptBaseClass.class)
makeApi(proxy, ScriptUtils.class)
makeApi(utils, UITools.class)
makeApi(utils, TextUtils.class)
makeApi(utils, FreeplaneVersion.class)
makeApi(utils, HtmlUtils.class)
makeApi(utils, LogUtils.class)
def web = createChild(newMap.root, WEB_NODE, 'http://freeplane.sourceforge.net/wiki/index.php/Scripting')
initHeading(web)
createChild(web, 'Groovy tutorials (Codehaus)', 'http://groovy.codehaus.org/Beginners+Tutorial')
createChild(web, 'Groovy presentation (Paul King)', 'http://www.asert.com/pubs/Groovy/Groovy.pdf')
createChild(web, 'Example scripts', 'http://freeplane.sourceforge.net/wiki/index.php/Scripting:_Example_scripts')
createChild(web, 'Scripting API changes', 'http://freeplane.sourceforge.net/wiki/index.php/Scripting:_API_Changes')
def legend = newMap.root.createChild(LEGEND_NODE)
initHeading(legend)
def methodLegend = legend.createChild("normal methods have a 'bookmark' icon")
methodLegend.icons.add('bookmark')
methodLegend.folded = true
def propertyLegend = legend.createChild("Groovy properties have a 'wizard' icon")
propertyLegend.icons.add('wizard')
propertyLegend.folded = true
def propertyBasics = propertyLegend.createChild("With properties you can write simpler expressions than with getters and setters")
propertyBasics.createChild(" node.text = 'Hello, world!'")
propertyBasics.createChild("instead of")
propertyBasics.createChild(" node.setText('Hello, world!')")
propertyLegend.createChild("read-only properties are indicated by a trailing (r)").
createChild('if (node.leaf)\n println "the id of this leaf node is " + node.id')
propertyLegend.createChild("write-only and read-write properties are indicated by a trailing (w) or (rw)").
createChild('node.text += " some suffix"')
propertyLegend.createChild("properties with differing type of setter and getter have two nodes")
legend.folded = true
c.deactivateUndo()
newMap.saved = true
|