diff --git a/README.md b/README.md
index 5129673..a1bfe7e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Cache UML Explorer
+# Cache Class Explorer
An UML Class explorer for InterSystems Caché.
##### Key features
@@ -6,6 +6,7 @@ An UML Class explorer for InterSystems Caché.
+ Build diagrams for any package or subpackage;
+ Edit diagrams after build;
+ Export diagrams as an image;
++ See Class methods, properties, parameters, SQL queries and more;
+ View class methods code with syntax highlighting;
+ Zoom in and out;
+ Search on diagram or in class tree;
diff --git a/cache/projectTemplate.xml b/cache/projectTemplate.xml
index af35049..43bfcb2 100644
--- a/cache/projectTemplate.xml
+++ b/cache/projectTemplate.xml
@@ -4,7 +4,7 @@
Cache UML Explorer vX.X.X/*build.replace:pkg.version*/
Class contains methods that return structured classes/packages data.
-63808,84964.699928
+63830,81286.756889
63653,67019.989197
@@ -93,6 +93,7 @@ Return structured data about class.
do oData.classes.%DispatchSetProperty(classDefinition.Name, oClass) // prevent from recursive setup
set package = $LISTTOSTRING($LIST($LISTFROMSTRING(classDefinition.Name, "."), 1, *-1),".")
set oProperties = ##class(%ZEN.proxyObject).%New()
+ set oQueries = ##class(%ZEN.proxyObject).%New()
set oClass.NAMESPACE = $NAMESPACE
set oClass.SYSTEM = classDefinition.System
@@ -109,57 +110,67 @@ Return structured data about class.
set oClass.properties = oProperties
set count = classDefinition.Properties.Count()
+ set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.PropertyDefinition")
for i=1:1:count {
set oProp = ##class(%ZEN.proxyObject).%New()
set p = classDefinition.Properties.GetAt(i)
do oProperties.%DispatchSetProperty(p.Name, oProp)
- set oProp.private = p.Private
- set oProp.readOnly = p.ReadOnly
- set oProp.cardinality = p.Cardinality
- set oProp.inverse = p.Inverse
+ for j=1:1:props.Properties.Count() {
+ set pname = props.Properties.GetAt(j).Name
+ set:(pname '= "parent") $PROPERTY(oProp, pname) = $PROPERTY(p, pname)
+ }
if (..classExists(package _ "." _ p.Type)) {
- set oProp.type = package _ "." _ p.Type
+ set oProp.Type = package _ "." _ p.Type
do ..fillClassData(oData, package _ "." _ p.Type)
} elseif (..classExists(..extendClassFromType(p.Type))) {
- set oProp.type = ..extendClassFromType(p.Type)
+ set oProp.Type = ..extendClassFromType(p.Type)
do ..fillClassData(oData, ..extendClassFromType(p.Type))
} else {
- set oProp.type = p.Type
+ set oProp.Type = ..extendClassFromType(p.Type)
}
}
set oMethods = ##class(%ZEN.proxyObject).%New()
set oClass.methods = oMethods
set count = classDefinition.Methods.Count()
+ set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.MethodDefinition")
for i=1:1:count {
set oMeth = ##class(%ZEN.proxyObject).%New()
set met = classDefinition.Methods.GetAt(i)
do oMethods.%DispatchSetProperty(met.Name, oMeth)
- set oMeth.private = met.Private
- set oMeth.returns = met.ReturnType
- set oMeth.classMethod = met.ClassMethod
- set oMeth.clientMethod = met.ClientMethod
- set oMeth.final = met.Final
- set oMeth.abstract = met.Abstract
- set oMeth.language = met.Language
- set oMeth.notInheritable = met.NotInheritable
- set oMeth.serverOnly = met.ServerOnly
- set oMeth.sqlProc = met.SqlProc
- set oMeth.sqlName = met.SqlName
- set oMeth.webMethod = met.WebMethod
- set oMeth.zenMethod = met.ZenMethod
+ for j=1:1:props.Properties.Count() {
+ set pname = props.Properties.GetAt(j).Name
+ set:((pname '= "parent") && (pname '= "Implementation")) $PROPERTY(oMeth, pname) = $PROPERTY(met, pname)
+ }
}
set oParameters = ##class(%ZEN.proxyObject).%New()
set oClass.parameters = oParameters
set count = classDefinition.Parameters.Count()
+ set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.ParameterDefinition")
for i=1:1:count {
set oPar = ##class(%ZEN.proxyObject).%New()
set p = classDefinition.Parameters.GetAt(i)
- set oPar.type = p.Type
+ for j=1:1:props.Properties.Count() {
+ set pname = props.Properties.GetAt(j).Name
+ set:(pname '= "parent") $PROPERTY(oPar, pname) = $PROPERTY(p, pname)
+ }
do oParameters.%DispatchSetProperty(p.Name, oPar)
}
+ #dim q as %Dictionary.QueryDefinition
+ set oClass.queries = oQueries
+ set props = ##class(%Dictionary.ClassDefinition).%OpenId("%Dictionary.QueryDefinition")
+ for i=1:1:classDefinition.Queries.Count() {
+ set oProp = ##class(%ZEN.proxyObject).%New()
+ set q = classDefinition.Queries.GetAt(i)
+ for j=1:1:props.Properties.Count() {
+ set pname = props.Properties.GetAt(j).Name
+ set:(pname '= "parent") $PROPERTY(oProp, pname) = $PROPERTY(q, pname)
+ }
+ do oQueries.%DispatchSetProperty(q.Name, oProp)
+ }
+
do ..collectInheritance(oData, oClass.super)
quit oClass
diff --git a/package.json b/package.json
index efb2207..0eeb735 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "CacheUMLExplorer",
- "version": "1.2.0",
+ "version": "1.3.0",
"description": "An UML Class explorer for InterSystems Caché",
"directories": {
"test": "test"
diff --git a/web/css/classView.css b/web/css/classView.css
index 7be2a4f..24ae0b2 100644
--- a/web/css/classView.css
+++ b/web/css/classView.css
@@ -19,7 +19,7 @@ svg {
/*fill: lightgray;*/
}
-.uml-class-attrs-rect, .uml-class-methods-rect {
+.uml-class-attrs-rect, .uml-class-methods-rect, .uml-class-queries-rect {
fill: white;
}
@@ -66,6 +66,12 @@ text {
fill: blue;
}
+.uml-class-queries-label {
+ font-family: monospace;
+ font-weight: 900;
+ fill: #0d0;
+}
+
.uml-class-params-label {
font-family: monospace;
font-weight: 900;
diff --git a/web/js/ClassView.js b/web/js/ClassView.js
index 0f869a9..f4d0733 100644
--- a/web/js/ClassView.js
+++ b/web/js/ClassView.js
@@ -237,8 +237,8 @@ ClassView.prototype.renderInfoGraphic = function () {
aggregation: {
"Persistent class (one)": {
"DataType class (many)": {
- left: "*",
- right: 1
+ left: "many",
+ right: "one"
}
}
},
@@ -409,19 +409,21 @@ ClassView.prototype.getClassSigns = function (classMetaData) {
*
* @param method
*/
-ClassView.prototype.getMethodIcons = function (method) {
+ClassView.prototype.getPropertyIcons = function (method) {
var icons = [];
- icons.push({ src: lib.image[method["private"] ? "minus" : "plus"] });
- if (method["abstract"]) icons.push({ src: lib.image.crystalBall });
- if (method["clientMethod"]) icons.push({ src: lib.image.user });
- if (method["final"]) icons.push({ src: lib.image.blueFlag });
- if (method["notInheritable"]) icons.push({ src: lib.image.redFlag });
- if (method["sqlProc"]) icons.push({ src: lib.image.table });
- if (method["webMethod"]) icons.push({ src: lib.image.earth });
- if (method["zenMethod"]) icons.push({ src: lib.image.zed });
- if (method["readOnly"]) icons.push({ src: lib.image.eye });
+ if (typeof method["Private"] !== "undefined") {
+ icons.push({ src: lib.image[method["Private"] ? "minus" : "plus"] });
+ }
+ if (method["Abstract"]) icons.push({ src: lib.image.crystalBall });
+ if (method["ClientMethod"]) icons.push({ src: lib.image.user });
+ if (method["Final"]) icons.push({ src: lib.image.blueFlag });
+ if (method["NotInheritable"]) icons.push({ src: lib.image.redFlag });
+ if (method["SqlProc"]) icons.push({ src: lib.image.table });
+ if (method["WebMethod"]) icons.push({ src: lib.image.earth });
+ if (method["ZenMethod"]) icons.push({ src: lib.image.zed });
+ if (method["ReadOnly"]) icons.push({ src: lib.image.eye });
return icons;
@@ -437,6 +439,7 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) {
var classParams = classMetaData["parameters"],
classProps = classMetaData["properties"],
classMethods = classMetaData["methods"],
+ classQueries = classMetaData["queries"],
keyWordsArray = [name],
self = this;
@@ -455,7 +458,8 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) {
for (n in params) {
keyWordsArray.push(n);
arr.push({
- text: n + (params[n]["type"] ? ": " + params[n]["type"] : "")
+ text: n + (params[n]["Type"] ? ": " + params[n]["Type"] : ""),
+ icons: self.getPropertyIcons(params[n])
});
}
return arr;
@@ -465,8 +469,8 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) {
for (n in ps) {
keyWordsArray.push(n);
arr.push({
- text: n + (ps[n]["type"] ? ": " + ps[n]["type"] : ""),
- icons: self.getMethodIcons(ps[n])
+ text: n + (ps[n]["Type"] ? ": " + ps[n]["Type"] : ""),
+ icons: self.getPropertyIcons(ps[n])
});
}
return arr;
@@ -476,18 +480,29 @@ ClassView.prototype.createClassInstance = function (name, classMetaData) {
for (n in met) {
keyWordsArray.push(n);
arr.push({
- text: n + (met[n]["returns"] ? ": " + met[n]["returns"] : ""),
+ text: n + (met[n]["ReturnType"] ? ": " + met[n]["ReturnType"] : ""),
styles: (function (t) {
return t ? { textDecoration: "underline" } : {}
})(met[n]["classMethod"]),
clickHandler: (function (n) {
return function () { self.showMethodCode(name, n); }
})(n),
- icons: self.getMethodIcons(met[n])
+ icons: self.getPropertyIcons(met[n])
});
}
return arr;
})(classMethods),
+ queries: (function (qrs) {
+ var arr = [], n;
+ for (n in qrs) {
+ keyWordsArray.push(n);
+ arr.push({
+ text: n,
+ icons: self.getPropertyIcons(qrs[n])
+ });
+ }
+ return arr;
+ })(classQueries),
classSigns: this.getClassSigns(classMetaData),
classType: classMetaData.$classType,
SYMBOL_12_WIDTH: self.SYMBOL_12_WIDTH
@@ -591,9 +606,11 @@ ClassView.prototype.render = function (data) {
ClassView.prototype.confirmRender = function (data) {
var self = this, p, pp, className,
+ LINK_TEXT_MARGIN = 22,
uml = joint.shapes.uml, relFrom, relTo,
classes = {}, connector;
+ console.log(data);
this.filterInherits(data);
// Reset view and zoom again because it may cause visual damage to icons.
@@ -648,8 +665,8 @@ ClassView.prototype.confirmRender = function (data) {
}
}
};
- if (link.left) arr.push(getLabel(link.left, 10));
- if (link.right) arr.push(getLabel(link.right, -10));
+ if (link.left) arr.push(getLabel(link.left, LINK_TEXT_MARGIN));
+ if (link.right) arr.push(getLabel(link.right, -LINK_TEXT_MARGIN));
return arr;
})(data[type][p][pp] || {})
}));
diff --git a/web/js/Logic.js b/web/js/Logic.js
index 5e270c6..c5ba36a 100644
--- a/web/js/Logic.js
+++ b/web/js/Logic.js
@@ -75,21 +75,21 @@ Logic.prototype.fillAssociations = function () {
if (!properties) continue;
for (propertyName in properties) {
po = properties[propertyName];
- if (po["cardinality"] === "one") {
- if (!aggr[po.type]) aggr[po.type] = {};
- aggr[po.type][className] = {
- left: "*",
- right: "1"
+ if (po["Cardinality"] === "one") {
+ if (!aggr[po["Type"]]) aggr[po["Type"]] = {};
+ aggr[po["Type"]][className] = {
+ left: "many",
+ right: "one"
};
- } else if (po["cardinality"] === "parent") {
- if (!compos[po.type]) compos[po.type] = {};
- compos[po.type][className] = {
- left: "*",
- right: "1"
+ } else if (po["Cardinality"] === "parent") {
+ if (!compos[po["Type"]]) compos[po["Type"]] = {};
+ compos[po["Type"]][className] = {
+ left: "child",
+ right: "parent"
};
- } else if (self.data.classes[po.type] && !po["cardinality"]) {
- if (!assoc[po.type]) assoc[po.type] = {};
- assoc[po.type][className] = {};
+ } else if (self.data.classes[po["Type"]] && !po["Cardinality"]) {
+ if (!assoc[po["Type"]]) assoc[po["Type"]] = {};
+ assoc[po["Type"]][className] = {};
}
}
}
diff --git a/web/jsLib/joint.shapes.uml.js b/web/jsLib/joint.shapes.uml.js
index 690d47b..644903a 100644
--- a/web/jsLib/joint.shapes.uml.js
+++ b/web/jsLib/joint.shapes.uml.js
@@ -22,11 +22,14 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
'Properties',
'',
'Methods',
+ '',
+ 'Queries',
'',
'',
'',
'',
'',
+ '',
''
].join(''),
@@ -46,6 +49,7 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
'.uml-class-params-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': 'white' },
'.uml-class-attrs-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' },
'.uml-class-methods-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' },
+ '.uml-class-queries-rect': { 'stroke': 'black', 'stroke-width': 1, 'fill': '#2980b9' },
'.uml-class-name-text': {
'ref': '.uml-class-name-rect', 'ref-y': .5, 'ref-x': .5, 'text-anchor': 'middle', 'y-alignment': 'middle', 'font-weight': 'bold',
@@ -63,6 +67,10 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
'ref': '.uml-class-methods-rect', 'ref-y': 5, 'ref-x': 5,
'fill': 'black', 'font-size': 12
},
+ '.uml-class-queries-text': {
+ 'ref': '.uml-class-queries-rect', 'ref-y': 5, 'ref-x': 5,
+ 'fill': 'black', 'font-size': 12
+ },
'.uml-class-attrs-label': {
ref: '.uml-class-attrs-label', fill: "black", 'font-size': 10,
xPos: -56
@@ -70,6 +78,9 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
'.uml-class-methods-label': {
ref: '.uml-class-methods-label', fill: "black", 'font-size': 10
},
+ '.uml-class-queries-label': {
+ ref: '.uml-class-queries-label', fill: "black", 'font-size': 10
+ },
'.uml-class-params-label': {
ref: '.uml-class-methods-label', fill: "black", 'font-size': 10
}
@@ -79,6 +90,7 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
params: [],
attributes: [],
methods: [],
+ queries: [],
classSigns: []
}, joint.shapes.basic.Generic.prototype.defaults),
@@ -90,7 +102,8 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
{ type: 'name', text: this.getClassName() },
{ type: 'params', text: (o = this.get('params')) , o: o },
{ type: 'attrs', text: (o = this.get('attributes')), o: o },
- { type: 'methods', text: (o = this.get('methods')) , o: o }
+ { type: 'methods', text: (o = this.get('methods')) , o: o },
+ { type: 'queries', text: (o = this.get('queries')) , o: o }
],
self = this,
classSigns = this.get('classSigns'),
@@ -185,7 +198,8 @@ joint.shapes.uml.Class = joint.shapes.basic.Generic.extend({
{ type: 'name', text: this.getClassName() },
{ type: 'params', text: this.get('params') },
{ type: 'attrs', text: this.get('attributes') },
- { type: 'methods', text: this.get('methods') }
+ { type: 'methods', text: this.get('methods') },
+ { type: 'queries', text: this.get('queries') }
];
var offsetY = 0;