Merge: modelize_class: Remove an unnecessary pre-condition
authorJean Privat <jean@pryen.org>
Mon, 8 Aug 2016 14:35:00 +0000 (10:35 -0400)
committerJean Privat <jean@pryen.org>
Mon, 8 Aug 2016 14:35:00 +0000 (10:35 -0400)
`build_classes` calls itself recursively on imported modules. So, there is no point to specify that imported modules must be processed first.

Signed-off-by: Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>

Pull-Request: #2233
Reviewed-by: Jean Privat <jean@pryen.org>

51 files changed:
contrib/benitlux/src/client/android.nit
contrib/crazy_moles/src/moles.nit
contrib/crazy_moles/src/moles_android.nit
examples/calculator/Makefile
examples/calculator/art/icon-ios-sci.svg [new file with mode: 0644]
examples/calculator/art/icon-sci.svg [new file with mode: 0644]
examples/calculator/src/calculator.nit
examples/calculator/src/scientific/android/.gitignore [new file with mode: 0644]
examples/calculator/src/scientific/ios/.gitignore [new file with mode: 0644]
examples/calculator/src/scientific/scientific.nit [moved from examples/calculator/src/scientific_calculator.nit with 90% similarity]
lib/android/game.nit
lib/android/nit_activity.nit
lib/android/ui/ui.nit
lib/app/README.md
lib/app/app_base.nit
lib/app/doc/Makefile [new file with mode: 0644]
lib/app/doc/app-nit-lifecycle.png [new file with mode: 0644]
lib/app/doc/app-nit-lifecycle.tex [new file with mode: 0644]
lib/app/ui.nit
lib/ios/app.nit
lib/linux/linux.nit
lib/linux/ui.nit
share/nitweb/directives/metrics/chart_properties.html [new file with mode: 0644]
share/nitweb/directives/metrics/metrics_list.html [new file with mode: 0644]
share/nitweb/index.html
share/nitweb/javascripts/entities.js
share/nitweb/javascripts/metrics.js [new file with mode: 0644]
share/nitweb/javascripts/model.js
share/nitweb/javascripts/nitweb.js
share/nitweb/stylesheets/nitweb.css
share/nitweb/vendors/d3pie.min.js [new file with mode: 0644]
share/nitweb/views/class.html
share/nitweb/views/group.html
share/nitweb/views/module.html
share/nitweb/views/package.html
src/ffi/java.nit
src/metrics/inheritance_metrics.nit
src/metrics/mclasses_metrics.nit
src/metrics/mendel_metrics.nit
src/metrics/mmodules_metrics.nit
src/metrics/nullables_metrics.nit
src/metrics/rta_metrics.nit
src/model/model_collect.nit
src/modelize/modelize_class.nit
src/nitweb.nit
src/platform/android.nit
src/platform/app_annotations.nit
src/platform/ios.nit
src/web/api_metrics.nit [new file with mode: 0644]
src/web/web.nit
tests/sav/nitmetrics_args1.res

index f3391bf..c009b50 100644 (file)
@@ -123,11 +123,10 @@ redef class SectionTitle
 end
 
 redef class ItemView
-       init do set_backgroud(native, app.native_context)
+       init do set_background(native, app.native_context)
 
-       private fun set_backgroud(view: NativeView, context: NativeContext) in "Java" `{
-               int color = context.getResources().getIdentifier("item_background", "color", context.getPackageName());
-               view.setBackgroundResource(color);
+       private fun set_background(view: NativeView, context: NativeContext) in "Java" `{
+               view.setBackgroundResource(R.color.item_background);
        `}
 end
 
@@ -141,9 +140,6 @@ end
 
 private fun native_notify(context: NativeService, id: Int, title, content: JavaString)
 in "Java" `{
-       int icon = context.getResources().getIdentifier(
-               "notif", "drawable", context.getPackageName());
-
        android.app.Notification.BigTextStyle style =
                new android.app.Notification.BigTextStyle();
        style.bigText(content);
@@ -156,7 +152,7 @@ in "Java" `{
        android.app.Notification notif = new android.app.Notification.Builder(context)
                .setContentTitle(title)
                .setContentText(content)
-               .setSmallIcon(icon)
+               .setSmallIcon(R.drawable.notif)
                .setAutoCancel(true)
                .setOngoing(false)
                .setStyle(style)
@@ -225,8 +221,7 @@ redef class BeerView
                                rating.setRating(final_rating);
 
                                // Header bar
-                               int icon = final_context.getResources().getIdentifier("notif", "drawable", final_context.getPackageName());
-                               dialog_builder.setIcon(icon);
+                               dialog_builder.setIcon(R.drawable.notif);
                                dialog_builder.setTitle(final_title);
 
                                // Rating control
index 505ce49..514b71f 100644 (file)
@@ -308,16 +308,11 @@ redef class App
        # Elapsed time since program launch
        var clock = new Clock
 
-       redef fun on_start
-       do
-               super
-               assets.load_all self
-       end
-
        redef fun on_create
        do
                super
 
+               assets.load_all self
                maximum_fps = 50.0
        end
 
index edf1251..c2c7f58 100644 (file)
@@ -29,7 +29,7 @@ redef class Game
 end
 
 redef class App
-       redef fun on_start
+       redef fun on_create
        do
                # We use as a reference the Moto X
                var tw = 720
index 8265899..bf2a19e 100644 (file)
@@ -7,27 +7,35 @@ bin/calculator: $(shell ${NITLS} -M src/calculator.nit linux) ${NITC}
        mkdir -p bin
        ${NITC} -o $@ src/calculator.nit -m linux
 
-bin/scientific_calculator: $(shell ${NITLS} -M src/scientific_calculator.nit linux) ${NITC}
+bin/scientific: $(shell ${NITLS} -M scientific linux) ${NITC}
        mkdir -p bin
-       ${NITC} -o $@ src/scientific_calculator.nit -m linux
+       ${NITC} -o $@ src/scientific -m linux
 
 # ---
 # Android
 
-android: bin/calculator.apk
+android: bin/calculator.apk bin/scientific.apk
 
-bin/calculator.apk: $(shell ${NITLS} -M src/scientific_calculator.nit src/android_calculator.nit) ${NITC} android/res/
+bin/calculator.apk: $(shell ${NITLS} -M src/android_calculator.nit) ${NITC} android/res/drawable-hdpi/icon.png
        mkdir -p bin
-       ${NITC} -o $@ src/scientific_calculator.nit -m src/android_calculator.nit -D debug
+       ${NITC} -o $@ src/android_calculator.nit -D debug
 
-android-release: $(shell ${NITLS} -M src/scientific_calculator.nit src/android_calculator.nit) ${NITC} android/res/
+bin/scientific.apk: $(shell ${NITLS} -M src/scientific src/android_calculator.nit) ${NITC} src/scientific/android/res/drawable-hdpi/icon.png
        mkdir -p bin
-       ${NITC} -o bin/calculator.apk src/scientific_calculator.nit -m src/android_calculator.nit --release
+       ${NITC} -o $@ src/scientific -m src/android_calculator.nit -D debug
 
-android/res/: art/icon.svg ../../contrib/inkscape_tools/bin/svg_to_icons
+android-release: $(shell ${NITLS} -M src/scientific src/android_calculator.nit) ${NITC} android/res/drawable-hdpi/icon.png
+       mkdir -p bin
+       ${NITC} -o bin/calculator.apk src/scientific -m src/android_calculator.nit --release
+
+android/res/drawable-hdpi/icon.png: art/icon.svg ../../contrib/inkscape_tools/bin/svg_to_icons
        mkdir -p android/res
        ../../contrib/inkscape_tools/bin/svg_to_icons art/icon.svg --android --out android/res/
 
+src/scientific/android/res/drawable-hdpi/icon.png: art/icon_sci.svg ../../contrib/inkscape_tools/bin/svg_to_icons
+       mkdir -p src/scientific/android/res
+       ../../contrib/inkscape_tools/bin/svg_to_icons art/icon-sci.svg --android --out src/scientific/android/res/
+
 ../../contrib/inkscape_tools/bin/svg_to_icons:
        make -C ../../contrib/inkscape_tools/
 
@@ -37,14 +45,24 @@ android-install: bin/calculator.apk
 # ---
 # iOS
 
-bin/calculator.app: $(shell ${NITLS} -M src/scientific_calculator.nit src/ios_calculator.nit) ${NITC} ios/AppIcon.appiconset/Contents.json
+ios: bin/calculator.app bin/scientific.app
+
+bin/calculator.app: $(shell ${NITLS} -M src/ios_calculator.nit) ${NITC} ios/AppIcon.appiconset/Contents.json
        mkdir -p bin
-       ${NITC} -o $@ src/scientific_calculator.nit -m src/ios_calculator.nit -D debug
+       ${NITC} -o $@ src/ios_calculator.nit -D debug
+
+bin/scientific.app: $(shell ${NITLS} -M src/scientific src/ios_calculator.nit) ${NITC} src/scientific/ios/AppIcon.appiconset/Contents.json
+       mkdir -p bin
+       ${NITC} -o $@ src/scientific -m src/ios_calculator.nit -D debug
 
 ios/AppIcon.appiconset/Contents.json: art/icon-ios.svg
        mkdir -p ios
        ../../contrib/inkscape_tools/bin/svg_to_icons art/icon-ios.svg --ios --out ios/AppIcon.appiconset/
 
+src/scientific/ios/AppIcon.appiconset/Contents.json: art/icon-ios.svg
+       mkdir -p ios
+       ../../contrib/inkscape_tools/bin/svg_to_icons art/icon-ios-sci.svg --ios --out src/scientific/ios/AppIcon.appiconset/
+
 # ---
 # Tests
 
diff --git a/examples/calculator/art/icon-ios-sci.svg b/examples/calculator/art/icon-ios-sci.svg
new file mode 100644 (file)
index 0000000..4ff6c5c
--- /dev/null
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="icon-ios-sci.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="1"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="388.94406"
+     inkscape:cy="238.25989"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1724"
+     inkscape:window-height="1103"
+     inkscape:window-x="242"
+     inkscape:window-y="23"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-540.36218)">
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:190.84666443px;line-height:125%;font-family:Sathu;-inkscape-font-specification:Sathu;letter-spacing:0px;word-spacing:0px;fill:#008bff;fill-opacity:1;stroke:none"
+       x="285.77982"
+       y="961.64594"
+       id="text3014"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3016"
+         x="285.77982"
+         y="961.64594">x²</tspan></text>
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:269.1137085px;line-height:125%;font-family:'Droid Sans';-inkscape-font-specification:'Droid Sans';letter-spacing:0px;word-spacing:0px;fill:#008bff;fill-opacity:1;stroke:none"
+       id="text3013"
+       transform="translate(4,-4)">
+      <path
+         d="m 77.43458,894.9284 0,-19.57907 121.41654,0 0,19.57907 -121.41654,0 m 0,53.87531 0,-19.71048 121.41654,0 0,19.71048 -121.41654,0"
+         style="fill:#008bff;fill-opacity:1"
+         id="path2999"
+         inkscape:connector-curvature="0" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:183.57151794px;line-height:125%;font-family:Sathu;-inkscape-font-specification:Sathu;letter-spacing:0px;word-spacing:0px;fill:#008bff;fill-opacity:1;stroke:none"
+       x="269.98895"
+       y="720.69153"
+       id="text2994"
+       sodipodi:linespacing="125%"
+       transform="scale(0.99205729,1.0080063)"><tspan
+         sodipodi:role="line"
+         id="tspan2996"
+         x="269.98895"
+         y="720.69153">√</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:190.84666443000000413px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#008bff;fill-opacity:1;stroke:none;font-family:Sathu;-inkscape-font-specification:Sathu;"
+       x="67.883858"
+       y="734.41699"
+       id="text2998"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3000"
+         x="67.883858"
+         y="734.41699">π</tspan></text>
+  </g>
+</svg>
diff --git a/examples/calculator/art/icon-sci.svg b/examples/calculator/art/icon-sci.svg
new file mode 100644 (file)
index 0000000..187bb2d
--- /dev/null
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="icon_sci.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.4"
+     inkscape:cx="164.13268"
+     inkscape:cy="278.8294"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1598"
+     inkscape:window-height="1316"
+     inkscape:window-x="2649"
+     inkscape:window-y="84"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-540.36218)">
+    <rect
+       style="fill:#4d4d4d;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect2987"
+       width="500"
+       height="500"
+       x="5.9999847"
+       y="546.36218"
+       rx="64"
+       ry="64" />
+    <rect
+       ry="48"
+       rx="48"
+       y="569.2193"
+       x="32.42857"
+       height="211.42856"
+       width="211.42856"
+       id="rect2989"
+       style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none" />
+    <rect
+       style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect2991"
+       width="211.42856"
+       height="211.42856"
+       x="268.14285"
+       y="569.2193"
+       rx="48"
+       ry="48" />
+    <rect
+       style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none"
+       id="rect2993"
+       width="211.42856"
+       height="211.42856"
+       x="32.42857"
+       y="806.36218"
+       rx="48"
+       ry="48" />
+    <rect
+       ry="48"
+       rx="48"
+       y="806.36218"
+       x="268.14285"
+       height="211.42856"
+       width="211.42856"
+       id="rect2995"
+       style="fill:#cccccc;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:5;stroke-miterlimit:4;stroke-dasharray:none" />
+    <g
+       style="font-size:269.1137085px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+       id="text3013">
+      <path
+         d="m 77.43458,894.9284 0,-19.57907 121.41654,0 0,19.57907 -121.41654,0 m 0,53.87531 0,-19.71048 121.41654,0 0,19.71048 -121.41654,0"
+         id="path2999"
+         style="" />
+    </g>
+    <text
+       xml:space="preserve"
+       style="font-size:190.84666443px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+       x="318.1875"
+       y="744.69647"
+       id="text2994"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2996"
+         x="318.1875"
+         y="744.69647">√</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:190.84666443px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+       x="72.978729"
+       y="725.84735"
+       id="text2998"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3000"
+         x="72.978729"
+         y="725.84735">π</tspan></text>
+    <flowRoot
+       xml:space="preserve"
+       id="flowRoot3002"
+       style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:New Shape;font-style:normal;font-weight:normal;font-size:16px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:New Shape;font-stretch:normal;font-variant:normal"><flowRegion
+         id="flowRegion3004"><rect
+           id="rect3006"
+           width="104.28571"
+           height="282.85715"
+           x="-225"
+           y="-62.285713" /></flowRegion><flowPara
+         id="flowPara3008"></flowPara></flowRoot>    <text
+       xml:space="preserve"
+       style="font-size:190.84666443px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
+       x="303.01755"
+       y="973.79059"
+       id="text3014"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3016"
+         x="303.01755"
+         y="973.79059">x²</tspan></text>
+  </g>
+</svg>
index 3b89ec7..6cfb1aa 100644 (file)
@@ -14,8 +14,8 @@
 
 # Portable calculator UI
 module calculator is
-       app_name "app.nit Calc."
-       app_version(0, 1, git_revision)
+       app_name "app.nit Calc"
+       app_version(0, 2, git_revision)
        app_namespace "org.nitlanguage.calculator"
 
        # Lock in portrait mode
diff --git a/examples/calculator/src/scientific/android/.gitignore b/examples/calculator/src/scientific/android/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
diff --git a/examples/calculator/src/scientific/ios/.gitignore b/examples/calculator/src/scientific/ios/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
 # limitations under the License.
 
 # Extends the portable calculator app with scientific operations
-module scientific_calculator
+module scientific is
+       app_name "app.nit Calc Sci"
+       app_namespace "org.nitlanguage.scientific_calculator"
+       app_files
+end
 
 import calculator
 
index 2d17054..d04e0f9 100644 (file)
@@ -27,7 +27,6 @@ redef class App
                super
                on_create
                on_restore_state
-               on_start
        end
 
        redef fun term_window
@@ -66,6 +65,4 @@ redef class App
                paused = false
                super
        end
-
-       redef fun destroy do on_destroy
 end
index 1668147..1bdf597 100644 (file)
@@ -207,6 +207,14 @@ redef class App
        end
 end
 
+redef class AppComponent
+       # The application is starting or restarting, it is visible to the user
+       fun on_start do end
+
+       # The application is being destroyed
+       fun on_destroy do end
+end
+
 # An Android activity
 #
 # You must implement the callbacks (prefixed with `on_`) to follow the
@@ -232,7 +240,7 @@ class Activity
        # Notification from Android, the activity has been restarted
        #
        # Followed by `on_start`.
-       fun on_restart do end
+       fun on_restart do app.on_restart
 
        # Notification from Android, the activity has been started
        #
index 7e14b19..66791c7 100644 (file)
@@ -79,6 +79,16 @@ redef class App
                native_activity.show_fragment(root_layout_id, window.native)
                super
        end
+
+       redef fun on_start do window.on_start
+
+       redef fun on_destroy do window.on_destroy
+end
+
+redef class CompositeControl
+       redef fun on_start do for i in items do i.on_start
+
+       redef fun on_destroy do for i in items do i.on_destroy
 end
 
 redef class Activity
index 0e0187b..4baecf4 100644 (file)
@@ -18,25 +18,25 @@ The _app.nit_ application life-cycle is compatible with all target platforms.
 It relies on the following sequence of events, represented here by their callback method name:
 
 1. `on_create`: The application is being created.
-   You should build the UI at this time.
+   You should build the UI at this time and launch services.
 
-2. `on_start`: The app is starting or restarting, background activities may
+2. `on_resume`: The app enters the active state, it is in the foreground and interactive.
 
-3. `on_resume`: The app enters the active state, it is in the foreground.
-
-4. `on_pause`: The app leaves the active state and the foreground.
+3. `on_pause`: The app becomes inactive and it leaves the foreground.
    It may still be visible in the background.
-   It may then go back to `on_resume` or `on_stop`.
 
-5. `on_stop`: The app is completely hidden.
-   It may then be destroyed (`on_destroy`) or go back to `on_start`.
+4. `on_stop`: The app is completely hidden.
+   It may then be destroyed (without warning) or go back to the active state with `on_restart`.
+
+5. `on_restart`: The app goes back to the inactive state.
+   You can revert what was done by `on_stop`.
 
-6. `on_destroy`: The app is being destroyed.
+![_app.nit_ life-cycle](doc/app-nit-lifecycle.png)
 
 Life-cycle events related to saving and restoring the application state are provided by two special callback methods:
 
 * `on_save_state`: The app may be destroyed soon, save its state for a future `on_restore_state`.
-  More on how it can be done in the `app::data_store` section.
+  There is more on how it can be done in the `app::data_store` section.
 
 * `on_restore_state`: The app is launching, restore its state from a previous `on_save_state`.
 
@@ -150,6 +150,17 @@ The _app.nit_ framework defines three annotations to customize the application p
   The special function `git_revision` will use the prefix of the hash of the latest git commit.
   By default, the version is 0.1.
 
+* `app_files` tells the compiler where to find platform specific resource files associated to a module.
+  By default, only the root of the project is searched for the folders `android` and `ios`.
+  The `android` folder is used as base for the generated Android project,
+  it can be used to specify the resource files, libs and even Java source files.
+  The `ios` folder is searched for icons only.
+
+  Each argument of `app_files` is a relative path to a folder containing extra `android` or `ios` folders.
+  If there is no arguments, the parent folder of the annotated module is used.
+  In case of name conflicts in the resource files, the files from the project root have the lowest priority,
+  those associated to modules lower in the importation hierarchy have higher priority.
+
 ## Usage Example
 
 ~~~
index e709e84..b2c26f5 100644 (file)
@@ -19,6 +19,7 @@ module app_base is
        new_annotation app_name
        new_annotation app_namespace
        new_annotation app_version
+       new_annotation app_files
 end
 
 # App subclasses are cross-platform applications
@@ -50,32 +51,57 @@ abstract class AppComponent
        # The application is being created
        #
        # You should build the UI at this time.
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onCreate`
+       # * iOS: `UIApplicationDelegate application:didFinishLaunchingWithOptions`
        fun on_create do end
 
-       # The application is starting or restarting, it is visible to the user
-       fun on_start do end
-
        # The application enters the active state, it is in the foreground and interactive
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onResume`
+       # * iOS: `UIApplicationDelegate applicationDidBecomeActive`
        fun on_resume do end
 
        # The application leaves the active state but is still partially visible
        #
-       # It may still be visible in the background.
        # It may then go back to `on_resume` or `on_stop`.
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onPause`
+       # * iOS: `UIApplicationDelegate applicationWillResignActive`
        fun on_pause do end
 
        # The application is completely hidden from the user
        #
-       # It may then be destroyed (`on_destroy`) or go back to `on_start`.
+       # It may then be destroyed or go back to a paused state with `on_restart`.
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onStop`
+       # * iOS: `UIApplicationDelegate applicationDidEnterBackground`
        fun on_stop do end
 
-       # The application is being destroyed
-       fun on_destroy do end
+       # The application returns to a visible state from a previous `on_stop`
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onRestart`
+       # * iOS: `UIApplicationDelegate applicationWillEnterForeground`
+       fun on_restart do end
 
        # The application may be destroyed soon, save its state for a future `on_restore_state`
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onSaveInstanceState`
+       # * iOS: `UIApplicationDelegate applicationDidEnterBackground`
        fun on_save_state do end
 
        # The application is launching, restore its state from a previous `on_save_state`
+       #
+       # Triggers are platform specific:
+       # * Android: `Activity.onCreate`, _not_ `Activity.onRestoreInstanceState`
+       #   as it is trigged only if there is a previous Android specific save state.
+       # * iOS: `UIApplicationDelegate applicationDidEnterBackground`
        fun on_restore_state do end
 end
 
diff --git a/lib/app/doc/Makefile b/lib/app/doc/Makefile
new file mode 100644 (file)
index 0000000..108bb91
--- /dev/null
@@ -0,0 +1,6 @@
+all:
+       rubber --pdf app-nit-lifecycle.tex
+       convert -density 130 app-nit-lifecycle.pdf -quality 90 app-nit-lifecycle.png
+
+clean:
+       rm -f *.log *.aux *.pdf
diff --git a/lib/app/doc/app-nit-lifecycle.png b/lib/app/doc/app-nit-lifecycle.png
new file mode 100644 (file)
index 0000000..b241c14
Binary files /dev/null and b/lib/app/doc/app-nit-lifecycle.png differ
diff --git a/lib/app/doc/app-nit-lifecycle.tex b/lib/app/doc/app-nit-lifecycle.tex
new file mode 100644 (file)
index 0000000..c0f2f48
--- /dev/null
@@ -0,0 +1,19 @@
+\documentclass[tikz]{standalone}
+
+\usetikzlibrary{automata,positioning}
+
+\begin{document}
+\begin{tikzpicture}[->,shorten >= 1pt,node distance=3cm,auto]
+       \node[state] (off)   {off};
+       \node[state] (inact) [right=of off,xshift=-0.5cm] {inactive};
+       \node[state] (act)   [below=of inact,yshift=2cm] {active};
+       \node[state] (back)  [right=of inact] {stopped};
+       \path[->]
+       (off)   edge [below] node {on\_create} (inact)
+       (inact) edge [left]  node {on\_resume} (act)
+               edge [below] node {on\_stop} (back)
+       (back)  edge [above] node {on\_restart}  (inact)
+               edge [bend right=20] node {}  (off)
+       (act)   edge [right] node {on\_pause}  (inact);
+\end{tikzpicture}
+\end{document}
index 5f66ab4..1fdf801 100644 (file)
@@ -56,16 +56,12 @@ redef class App
 
        redef fun on_create do window.on_create
 
-       redef fun on_start do window.on_start
-
        redef fun on_resume do window.on_resume
 
        redef fun on_pause do window.on_pause
 
        redef fun on_stop do window.on_stop
 
-       redef fun on_destroy do window.on_destroy
-
        redef fun on_restore_state do window.on_restore_state
 
        redef fun on_save_state do window.on_save_state
@@ -165,16 +161,12 @@ class CompositeControl
 
        redef fun on_create do for i in items do i.on_create
 
-       redef fun on_start do for i in items do i.on_start
-
        redef fun on_resume do for i in items do i.on_resume
 
        redef fun on_pause do for i in items do i.on_pause
 
        redef fun on_stop do for i in items do i.on_stop
 
-       redef fun on_destroy do for i in items do i.on_destroy
-
        redef fun on_restore_state do for i in items do i.on_restore_state
 
        redef fun on_save_state do for i in items do i.on_save_state
index b0ffb30..3070868 100644 (file)
@@ -178,7 +178,7 @@ redef class App
        # inactive state.
        #
        # Redef to undo changes made on entering the background.
-       fun will_enter_foreground do on_start
+       fun will_enter_foreground do on_restart
 
        # The application just became active
        #
@@ -196,7 +196,6 @@ redef class App
                on_save_state
                on_pause
                on_stop
-               on_destroy
        end
 end
 
index 6b89eb8..e958e2d 100644 (file)
@@ -32,7 +32,6 @@ redef class App
 
                on_create
                on_restore_state
-               on_start
                on_resume
        end
 
@@ -43,7 +42,6 @@ redef class App
                on_pause
                on_save_state
                on_stop
-               on_destroy
        end
 end
 
index 2c0eec1..8a6cc81 100644 (file)
@@ -64,7 +64,6 @@ redef class App
        do
                app.on_create
                app.on_restore_state
-               app.on_start
                app.on_resume
 
                gtk_main
@@ -72,7 +71,6 @@ redef class App
                app.on_pause
                app.on_stop
                app.on_save_state
-               app.on_destroy
        end
 
        # Spacing between GTK controls, default at 2
diff --git a/share/nitweb/directives/metrics/chart_properties.html b/share/nitweb/directives/metrics/chart_properties.html
new file mode 100644 (file)
index 0000000..5e80e02
--- /dev/null
@@ -0,0 +1 @@
+<div id="{{chartId}}"></div>
diff --git a/share/nitweb/directives/metrics/metrics_list.html b/share/nitweb/directives/metrics/metrics_list.html
new file mode 100644 (file)
index 0000000..2901b5b
--- /dev/null
@@ -0,0 +1,57 @@
+<div class='card'>
+       <div class='card-heading'>
+               <h3 class='card-title'>{{listTitle}}</h3>
+       </div>
+       <div class='card-body'>
+               <table class='table'>
+                       <tr>
+                               <th></th>
+                               <th>avg.</th>
+                               <th>std dev.</th>
+                               <th>min.</th>
+                               <th>max.</th>
+                       </tr>
+                       <tr ng-repeat='metric in listMetricsNames'>
+                               <th>
+                                       <abbr title="{{listMetrics[metric].desc}}">
+                                               {{listMetrics[metric].name.toUpperCase()}}
+                                       </abbr>
+                               </th>
+                               <td ng-if='listMetrics[metric].empty' colspan='5'>No classes in this module</td>
+                               <td>{{listMetrics[metric].avg}}</td>
+                               <td>{{listMetrics[metric].std_dev}}</td>
+                               <td ng-if='!listMetrics[metric].empty'>
+                                       {{listMetrics[metric].values[listMetrics[metric].min.full_name].value}}
+                                       &nbsp;(<entity-link mentity='listMetrics[metric].min' />)
+                               </td>
+                               <td ng-if='!listMetrics[metric].empty'>
+                                       {{listMetrics[metric].values[listMetrics[metric].max.full_name].value}}
+                                       &nbsp;(<entity-link mentity='listMetrics[metric].max' />)
+                               </td>
+                       </tr>
+               </table>
+               <p class='text-center'>
+                       <button class="btn btn-link" type="button" data-toggle="collapse" data-target="#{{listId}}" aria-expanded="false" aria-controls="{{listId}}">
+                               Show all values
+                       </button>
+               </p>
+               <div class="collapse" id="{{listId}}">
+                       <table class='table'>
+                               <tr>
+                                       <th></th>
+                                       <th ng-repeat='metric in listMetricsNames'>
+                                               <abbr title="{{listMetrics[metric].desc}}">
+                                                       {{listMetrics[metric].name.toUpperCase()}}
+                                               </abbr>
+                                       </th>
+                               </tr>
+                               <tr ng-repeat='value in listMetrics[listMetricsDefault].values'>
+                                       <td><entity-link mentity='value.mentity'></td>
+                                       <td ng-repeat='metric in listMetricsNames'>
+                                               {{listMetrics[metric].values[value.mentity.full_name].value}}
+                                       </td>
+                               </tr>
+                       </table>
+               </div>
+       </div>
+</div>
index c615d41..b936f1d 100644 (file)
@@ -17,6 +17,9 @@
 
                <link href='/stylesheets/nitweb_bootstrap.css' rel='stylesheet'>
                <link href='/stylesheets/nitweb.css' rel='stylesheet'>
+
+               <script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.4/d3.min.js"></script>
+               <script src="/vendors/d3pie.min.js"></script>
        </head>
        <body>
                <nav class='navbar navbar-default navbar-fixed-top'>
@@ -70,5 +73,6 @@
                <script src='/javascripts/ui.js'></script>
                <script src='/javascripts/index.js'></script>
                <script src='/javascripts/docdown.js'></script>
+               <script src='/javascripts/metrics.js'></script>
        </body>
 </html>
index 2438d84..67c68bd 100644 (file)
@@ -18,7 +18,7 @@
        angular
                .module('entities', ['ngSanitize', 'ui', 'model'])
 
-               .controller('EntityCtrl', ['Model', '$routeParams', '$scope', '$sce', function(Model, $routeParams, $scope, $sce) {
+               .controller('EntityCtrl', ['Model', 'Metrics', '$routeParams', '$scope', '$sce', function(Model, Metrics, $routeParams, $scope, $sce) {
                        $scope.entityId = $routeParams.id;
 
                        this.loadEntityLinearization = function() {
                                        });
                        };
 
+                       this.loadStructuralMetrics = function() {
+                               Metrics.loadStructuralMetrics($routeParams.id,
+                                       function(data) {
+                                               $scope.metrics = data;
+                                       }, function(message, status) {
+                                               $scope.error = {message: message, status: status};
+                                       });
+                       };
+
                        Model.loadEntity($routeParams.id,
                                function(data) {
                                        $scope.mentity = data;
diff --git a/share/nitweb/javascripts/metrics.js b/share/nitweb/javascripts/metrics.js
new file mode 100644 (file)
index 0000000..9698e81
--- /dev/null
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function() {
+       angular
+               .module('metrics', ['model'])
+
+               .directive('metricsList', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       listId: '@',
+                                       listTitle: '@',
+                                       listMetrics: '=',
+                                       listMetricsNames: '=',
+                                       listMetricsDefault: '='
+                               },
+                               templateUrl: '/directives/metrics/metrics_list.html'
+                       };
+               })
+
+               .directive('chartModuleDefinitionsKind', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       chartId: '@',
+                                       chartMetrics: '='
+                               },
+                               templateUrl: '/directives/metrics/chart_properties.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.loadChart = function() {
+                                               if($scope.chart) { return; }
+                                               $scope.chart = new d3pie($scope.chartId, {
+                                                       "header": {
+                                                               "title": {
+                                                                       "fontSize": 24,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "subtitle": {
+                                                                       "color": "#999999",
+                                                                       "fontSize": 12,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "titleSubtitlePadding": 9
+                                                       },
+                                                       "size": {
+                                                               "canvasHeight": 200,
+                                                               "canvasWidth": 350,
+                                                               "pieOuterRadius": "80%"
+                                                       },
+                                                       "data": {
+                                                               "sortOrder": "value-asc",
+                                                               "content": [
+                                                                       {
+                                                                               "label": "Concrete classes",
+                                                                               "value": $scope.chartMetrics.mnbcc.avg,
+                                                                               "color": "#228835"
+                                                                       },
+                                                                       {
+                                                                               "label": "Abstract classes",
+                                                                               "value": $scope.chartMetrics.mnbac.avg,
+                                                                               "color": "#103EB8"
+                                                                       },
+                                                                       {
+                                                                               "label": "Interfaces",
+                                                                               "value": $scope.chartMetrics.mnbic.avg,
+                                                                               "color": "#e65314"
+                                                                       }
+                                                               ]
+                                                       },
+                                                       "labels": {
+                                                               "outer": {
+                                                                       "format": "label-value2",
+                                                                       "pieDistance": 20
+                                                               },
+                                                               "inner": {
+                                                                       "hideWhenLessThanPercentage": 3
+                                                               },
+                                                               "mainLabel": {
+                                                                       "fontSize": 11
+                                                               },
+                                                               "percentage": {
+                                                                       "color": "#ffffff",
+                                                                       "decimalPlaces": 0
+                                                               },
+                                                               "value": {
+                                                                       "color": "#adadad",
+                                                                       "fontSize": 11
+                                                               },
+                                                               "lines": {
+                                                                       "enabled": true,
+                                                                       "style": "straight"
+                                                               },
+                                                               "truncation": {
+                                                                       "enabled": true
+                                                               }
+                                                       }
+                                               });
+                                       };
+
+                                       $scope.$watch('chartMetrics', function(nv, ov) {
+                                               if(nv) {
+                                                       setTimeout($scope.loadChart, 100);
+                                               }
+                                       });
+                               }
+                       };
+               })
+
+               .directive('chartModuleDefinitionsInh', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       chartId: '@',
+                                       chartMetrics: '='
+                               },
+                               templateUrl: '/directives/metrics/chart_properties.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.loadChart = function() {
+                                               if($scope.chart) { return; }
+                                               $scope.chart = new d3pie($scope.chartId, {
+                                                       "header": {
+                                                               "title": {
+                                                                       "fontSize": 24,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "subtitle": {
+                                                                       "color": "#999999",
+                                                                       "fontSize": 12,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "titleSubtitlePadding": 9
+                                                       },
+                                                       "size": {
+                                                               "canvasHeight": 200,
+                                                               "canvasWidth": 350,
+                                                               "pieOuterRadius": "80%"
+                                                       },
+                                                       "data": {
+                                                               "sortOrder": "value-asc",
+                                                               "content": [
+                                                                       {
+                                                                               "label": "Inherited",
+                                                                               "value": $scope.chartMetrics.mnbd.avg - $scope.chartMetrics.mnbr.avg - $scope.chartMetrics.mnbi.avg,
+                                                                               "color": "#999999"
+                                                                       },
+                                                                       {
+                                                                               "label": "Introduced",
+                                                                               "value": $scope.chartMetrics.mnbi.avg,
+                                                                               "color": "#228835"
+                                                                       },
+                                                                       {
+                                                                               "label": "Redefined",
+                                                                               "value": $scope.chartMetrics.mnbr.avg,
+                                                                               "color": "#e65314"
+                                                                       }
+                                                               ]
+                                                       },
+                                                       "labels": {
+                                                               "outer": {
+                                                                       "format": "label-value2",
+                                                                       "pieDistance": 20
+                                                               },
+                                                               "inner": {
+                                                                       "hideWhenLessThanPercentage": 3
+                                                               },
+                                                               "mainLabel": {
+                                                                       "fontSize": 11
+                                                               },
+                                                               "percentage": {
+                                                                       "color": "#ffffff",
+                                                                       "decimalPlaces": 0
+                                                               },
+                                                               "value": {
+                                                                       "color": "#adadad",
+                                                                       "fontSize": 11
+                                                               },
+                                                               "lines": {
+                                                                       "enabled": true,
+                                                                       "style": "straight"
+                                                               },
+                                                               "truncation": {
+                                                                       "enabled": true
+                                                               }
+                                                       }
+                                               });
+                                       };
+
+                                       $scope.$watch('chartMetrics', function(nv, ov) {
+                                               if(nv) {
+                                                       setTimeout($scope.loadChart, 100);
+                                               }
+                                       });
+                               }
+                       };
+               })
+
+               .directive('chartClassPropertiesInh', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       chartId: '@',
+                                       chartMetrics: '='
+                               },
+                               templateUrl: '/directives/metrics/chart_properties.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.loadChart = function() {
+                                               if($scope.chart) { return; }
+                                               $scope.chart = new d3pie($scope.chartId, {
+                                                       "header": {
+                                                               "title": {
+                                                                       "fontSize": 24,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "subtitle": {
+                                                                       "color": "#999999",
+                                                                       "fontSize": 12,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "titleSubtitlePadding": 9
+                                                       },
+                                                       "size": {
+                                                               "canvasHeight": 200,
+                                                               "canvasWidth": 350,
+                                                               "pieOuterRadius": "80%"
+                                                       },
+                                                       "data": {
+                                                               "sortOrder": "value-asc",
+                                                               "content": [
+                                                                       {
+                                                                               "label": "Inherited",
+                                                                               "value": $scope.chartMetrics.cnbhp.avg - $scope.chartMetrics.cnbrp.avg,
+                                                                               "color": "#999999"
+                                                                       },
+                                                                       {
+                                                                               "label": "Introduced",
+                                                                               "value": $scope.chartMetrics.cnbip.avg,
+                                                                               "color": "#228835"
+                                                                       },
+                                                                       {
+                                                                               "label": "Redefined",
+                                                                               "value": $scope.chartMetrics.cnbrp.avg,
+                                                                               "color": "#e65314"
+                                                                       }
+                                                               ]
+                                                       },
+                                                       "labels": {
+                                                               "outer": {
+                                                                       "format": "label-value2",
+                                                                       "pieDistance": 20
+                                                               },
+                                                               "inner": {
+                                                                       "hideWhenLessThanPercentage": 3
+                                                               },
+                                                               "mainLabel": {
+                                                                       "fontSize": 11
+                                                               },
+                                                               "percentage": {
+                                                                       "color": "#ffffff",
+                                                                       "decimalPlaces": 0
+                                                               },
+                                                               "value": {
+                                                                       "color": "#adadad",
+                                                                       "fontSize": 11
+                                                               },
+                                                               "lines": {
+                                                                       "enabled": true,
+                                                                       "style": "straight"
+                                                               },
+                                                               "truncation": {
+                                                                       "enabled": true
+                                                               }
+                                                       }
+                                               });
+                                       };
+
+                                       $scope.$watch('chartMetrics', function(nv, ov) {
+                                               if(nv) {
+                                                       setTimeout($scope.loadChart, 100);
+                                               }
+                                       });
+                               }
+                       };
+               })
+
+               .directive('chartClassPropertiesKind', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       chartId: '@',
+                                       chartMetrics: '='
+                               },
+                               templateUrl: '/directives/metrics/chart_properties.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.loadChart = function() {
+                                               if($scope.chart) { return; }
+                                               $scope.chart = new d3pie($scope.chartId, {
+                                                       "header": {
+                                                               "title": {
+                                                                       "fontSize": 24,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "subtitle": {
+                                                                       "color": "#999999",
+                                                                       "fontSize": 12,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "titleSubtitlePadding": 9
+                                                       },
+                                                       "size": {
+                                                               "canvasHeight": 200,
+                                                               "canvasWidth": 350,
+                                                               "pieOuterRadius": "80%"
+                                                       },
+                                                       "data": {
+                                                               "sortOrder": "value-asc",
+                                                               "content": [
+                                                                       {
+                                                                               "label": "Attributes",
+                                                                               "value": $scope.chartMetrics.cnba.avg,
+                                                                               "color": "#228835"
+                                                                       },
+                                                                       {
+                                                                               "label": "Methods",
+                                                                               "value": $scope.chartMetrics.cnbm.avg - $scope.chartMetrics.cnbi.avg,
+                                                                               "color": "#999999"
+                                                                       },
+                                                                       {
+                                                                               "label": "Constructors",
+                                                                               "value": $scope.chartMetrics.cnbi.avg,
+                                                                               "color": "#e65314"
+                                                                       },
+                                                                       {
+                                                                               "label": "Virtual Types",
+                                                                               "value": $scope.chartMetrics.cnbv.avg,
+                                                                               "color": "#103EB8"
+                                                                       }
+                                                               ]
+                                                       },
+                                                       "labels": {
+                                                               "outer": {
+                                                                       "format": "label-value2",
+                                                                       "pieDistance": 20
+                                                               },
+                                                               "inner": {
+                                                                       "hideWhenLessThanPercentage": 3
+                                                               },
+                                                               "mainLabel": {
+                                                                       "fontSize": 11
+                                                               },
+                                                               "percentage": {
+                                                                       "color": "#ffffff",
+                                                                       "decimalPlaces": 0
+                                                               },
+                                                               "value": {
+                                                                       "color": "#adadad",
+                                                                       "fontSize": 11
+                                                               },
+                                                               "lines": {
+                                                                       "enabled": true,
+                                                                       "style": "straight"
+                                                               },
+                                                               "truncation": {
+                                                                       "enabled": true
+                                                               }
+                                                       }
+                                               });
+                                       };
+
+                                       $scope.$watch('chartMetrics', function(nv, ov) {
+                                               if(nv) {
+                                                       setTimeout($scope.loadChart, 100);
+                                               }
+                                       });
+                               }
+                       };
+               })
+
+               .directive('chartClassInheritanceKind', function() {
+                       return {
+                               restrict: 'E',
+                               scope: {
+                                       chartId: '@',
+                                       chartMetrics: '='
+                               },
+                               templateUrl: '/directives/metrics/chart_properties.html',
+                               link: function ($scope, element, attrs) {
+                                       $scope.loadChart = function() {
+                                               if($scope.chart) { return; }
+                                               $scope.chart = new d3pie($scope.chartId, {
+                                                       "header": {
+                                                               "title": {
+                                                                       "fontSize": 24,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "subtitle": {
+                                                                       "color": "#999999",
+                                                                       "fontSize": 12,
+                                                                       "font": "open sans"
+                                                               },
+                                                               "titleSubtitlePadding": 9
+                                                       },
+                                                       "size": {
+                                                               "canvasHeight": 200,
+                                                               "canvasWidth": 350,
+                                                               "pieOuterRadius": "80%"
+                                                       },
+                                                       "data": {
+                                                               "sortOrder": "value-asc",
+                                                               "content": [
+                                                                       {
+                                                                               "label": "Interfaces",
+                                                                               "value": $scope.chartMetrics.cnoai.avg,
+                                                                               "color": "#228835"
+                                                                       },
+                                                                       {
+                                                                               "label": "Abstract classes",
+                                                                               "value": $scope.chartMetrics.cnoaa.avg,
+                                                                               "color": "#103EB8"
+                                                                       },
+                                                                       {
+                                                                               "label": "Concrete classes",
+                                                                               "value": $scope.chartMetrics.cnoac.avg - $scope.chartMetrics.cnoaa.avg,
+                                                                               "color": "#e65314"
+                                                                       }
+                                                               ]
+                                                       },
+                                                       "labels": {
+                                                               "outer": {
+                                                                       "format": "label-value2",
+                                                                       "pieDistance": 20
+                                                               },
+                                                               "inner": {
+                                                                       "hideWhenLessThanPercentage": 3
+                                                               },
+                                                               "mainLabel": {
+                                                                       "fontSize": 11
+                                                               },
+                                                               "percentage": {
+                                                                       "color": "#ffffff",
+                                                                       "decimalPlaces": 0
+                                                               },
+                                                               "value": {
+                                                                       "color": "#adadad",
+                                                                       "fontSize": 11
+                                                               },
+                                                               "lines": {
+                                                                       "enabled": true,
+                                                                       "style": "straight"
+                                                               },
+                                                               "truncation": {
+                                                                       "enabled": true
+                                                               }
+                                                       }
+                                               });
+                                       };
+
+                                       $scope.$watch('chartMetrics', function(nv, ov) {
+                                               if(nv) {
+                                                       setTimeout($scope.loadChart, 100);
+                                               }
+                                       });
+                               }
+                       };
+               })
+})();
index efcecd5..cd127ed 100644 (file)
                                }
                        }
                }])
+
+               .factory('Metrics', [ '$http', function($http) {
+                       return {
+                               loadStructuralMetrics: function(id, cb, cbErr) {
+                                       $http.get(apiUrl + '/metrics/structural/' + id)
+                                               .success(cb)
+                                               .error(cbErr);
+                               }
+                       }
+               }])
 })();
index 7fa1142..9dac463 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 (function() {
-       angular.module('nitweb', ['ngRoute', 'ngSanitize', 'angular-loading-bar', 'entities', 'docdown', 'index'])
+       angular.module('nitweb', ['ngRoute', 'ngSanitize', 'angular-loading-bar', 'entities', 'docdown', 'index', 'metrics'])
        .config(['cfpLoadingBarProvider', function(cfpLoadingBarProvider) {
                cfpLoadingBarProvider.includeSpinner = false;
        }])
index db8a03d..0fcff6c 100644 (file)
@@ -75,6 +75,10 @@ a {
        border-top: none;
 }
 
+.card-title {
+    padding-left: 15px;
+}
+
 /* ui */
 
 .btn-bar { margin-top: -5px; float: right }
diff --git a/share/nitweb/vendors/d3pie.min.js b/share/nitweb/vendors/d3pie.min.js
new file mode 100644 (file)
index 0000000..e27e069
--- /dev/null
@@ -0,0 +1,9 @@
+/*!
+* d3pie
+* @author Ben Keen
+* @version 0.1.9
+* @date April 2015
+* @repo http://github.com/benkeen/d3pie
+*/
+!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.d3pie=b(a)}(this,function(){var a="d3pie",b="0.1.6",c=0,d={header:{title:{text:"",color:"#333333",fontSize:18,font:"arial"},subtitle:{text:"",color:"#666666",fontSize:14,font:"arial"},location:"top-center",titleSubtitlePadding:8},footer:{text:"",color:"#666666",fontSize:14,font:"arial",location:"left"},size:{canvasHeight:500,canvasWidth:500,pieInnerRadius:"0%",pieOuterRadius:null},data:{sortOrder:"none",ignoreSmallSegments:{enabled:!1,valueType:"percentage",value:null},smallSegmentGrouping:{enabled:!1,value:1,valueType:"percentage",label:"Other",color:"#cccccc"},content:[]},labels:{outer:{format:"label",hideWhenLessThanPercentage:null,pieDistance:30},inner:{format:"percentage",hideWhenLessThanPercentage:null},mainLabel:{color:"#333333",font:"arial",fontSize:10},percentage:{color:"#dddddd",font:"arial",fontSize:10,decimalPlaces:0},value:{color:"#cccc44",font:"arial",fontSize:10},lines:{enabled:!0,style:"curved",color:"segment"},truncation:{enabled:!1,truncateLength:30},formatter:null},effects:{load:{effect:"default",speed:1e3},pullOutSegmentOnClick:{effect:"bounce",speed:300,size:10},highlightSegmentOnMouseover:!0,highlightLuminosity:-.2},tooltips:{enabled:!1,type:"placeholder",string:"",placeholderParser:null,styles:{fadeInSpeed:250,backgroundColor:"#000000",backgroundOpacity:.5,color:"#efefef",borderRadius:2,font:"arial",fontSize:10,padding:4}},misc:{colors:{background:null,segments:["#2484c1","#65a620","#7b6888","#a05d56","#961a1a","#d8d23a","#e98125","#d0743c","#635222","#6ada6a","#0c6197","#7d9058","#207f33","#44b9b0","#bca44a","#e4a14b","#a3acb2","#8cc3e9","#69a6f9","#5b388f","#546e91","#8bde95","#d2ab58","#273c71","#98bf6e","#4daa4b","#98abc5","#cc1010","#31383b","#006391","#c2643f","#b0a474","#a5a39c","#a9c2bc","#22af8c","#7fcecf","#987ac6","#3d3b87","#b77b1c","#c9c2b6","#807ece","#8db27c","#be66a2","#9ed3c6","#00644b","#005064","#77979f","#77e079","#9c73ab","#1f79a7"],segmentStroke:"#ffffff"},gradient:{enabled:!1,percentage:95,color:"#000000"},canvasPadding:{top:5,right:5,bottom:5,left:5},pieCenterOffset:{x:0,y:0},cssPrefix:null},callbacks:{onload:null,onMouseoverSegment:null,onMouseoutSegment:null,onClickSegment:null}},e={initialCheck:function(a){var b=a.cssPrefix,c=a.element,d=a.options;if(!window.d3||!window.d3.hasOwnProperty("version"))return console.error("d3pie error: d3 is not available"),!1;if(!(c instanceof HTMLElement||c instanceof SVGElement))return console.error("d3pie error: the first d3pie() param must be a valid DOM element (not jQuery) or a ID string."),!1;if(!/[a-zA-Z][a-zA-Z0-9_-]*$/.test(b))return console.error("d3pie error: invalid options.misc.cssPrefix"),!1;if(!f.isArray(d.data.content))return console.error("d3pie error: invalid config structure: missing data.content property."),!1;if(0===d.data.content.length)return console.error("d3pie error: no data supplied."),!1;for(var e=[],g=0;g<d.data.content.length;g++)"number"!=typeof d.data.content[g].value||isNaN(d.data.content[g].value)?console.log("not valid: ",d.data.content[g]):d.data.content[g].value<=0?console.log("not valid - should have positive value: ",d.data.content[g]):e.push(d.data.content[g]);return a.options.data.content=e,!0}},f={addSVGSpace:function(a){var b=a.element,c=a.options.size.canvasWidth,d=a.options.size.canvasHeight,e=a.options.misc.colors.background,f=d3.select(b).append("svg:svg").attr("width",c).attr("height",d);return"transparent"!==e&&f.style("background-color",function(){return e}),f},whenIdExists:function(a,b){var c=1,d=1e3,e=setInterval(function(){document.getElementById(a)&&(clearInterval(e),b()),c>d&&clearInterval(e),c++},1)},whenElementsExist:function(a,b){var c=1,d=1e3,e=setInterval(function(){for(var f=!0,g=0;g<a.length;g++)if(!document.getElementById(a[g])){f=!1;break}f&&(clearInterval(e),b()),c>d&&clearInterval(e),c++},1)},shuffleArray:function(a){for(var b,c,d=a.length;0!==d;)c=Math.floor(Math.random()*d),d-=1,b=a[d],a[d]=a[c],a[c]=b;return a},processObj:function(a,b,c){return"string"==typeof b?f.processObj(a,b.split("."),c):1===b.length&&void 0!==c?(a[b[0]]=c,a[b[0]]):0===b.length?a:f.processObj(a[b[0]],b.slice(1),c)},getDimensions:function(a){var b=document.getElementById(a),c=0,d=0;if(b){var e=b.getBBox();c=e.width,d=e.height}else console.log("error: getDimensions() "+a+" not found.");return{w:c,h:d}},rectIntersect:function(a,b){var c=b.x>a.x+a.w||b.x+b.w<a.x||b.y+b.h<a.y||b.y>a.y+a.h;return!c},getColorShade:function(a,b){a=String(a).replace(/[^0-9a-f]/gi,""),a.length<6&&(a=a[0]+a[0]+a[1]+a[1]+a[2]+a[2]),b=b||0;for(var c="#",d=0;3>d;d++){var e=parseInt(a.substr(2*d,2),16);e=Math.round(Math.min(Math.max(0,e+e*b),255)).toString(16),c+=("00"+e).substr(e.length)}return c},initSegmentColors:function(a){for(var b=a.options.data.content,c=a.options.misc.colors.segments,d=[],e=0;e<b.length;e++)d.push(b[e].hasOwnProperty("color")?b[e].color:c[e]);return d},applySmallSegmentGrouping:function(a,b){var c;"percentage"===b.valueType&&(c=h.getTotalPieSize(a));for(var d=[],e=[],f=0,g=0;g<a.length;g++)if("percentage"===b.valueType){var i=a[g].value/c*100;if(i<=b.value){e.push(a[g]),f+=a[g].value;continue}a[g].isGrouped=!1,d.push(a[g])}else{if(a[g].value<=b.value){e.push(a[g]),f+=a[g].value;continue}a[g].isGrouped=!1,d.push(a[g])}return e.length&&d.push({color:b.color,label:b.label,value:f,isGrouped:!0,groupedData:e}),d},showPoint:function(a,b,c){a.append("circle").attr("cx",b).attr("cy",c).attr("r",2).style("fill","black")},isFunction:function(a){var b={};return a&&"[object Function]"===b.toString.call(a)},isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)}},g=function(){var a,b,c,d,e,f,h=arguments[0]||{},i=1,j=arguments.length,k=!1,l=Object.prototype.toString,m=Object.prototype.hasOwnProperty,n={"[object Boolean]":"boolean","[object Number]":"number","[object String]":"string","[object Function]":"function","[object Array]":"array","[object Date]":"date","[object RegExp]":"regexp","[object Object]":"object"},o={isFunction:function(a){return"function"===o.type(a)},isArray:Array.isArray||function(a){return"array"===o.type(a)},isWindow:function(a){return null!==a&&a===a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return null===a?String(a):n[l.call(a)]||"object"},isPlainObject:function(a){if(!a||"object"!==o.type(a)||a.nodeType)return!1;try{if(a.constructor&&!m.call(a,"constructor")&&!m.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}var c;for(c in a);return void 0===c||m.call(a,c)}};for("boolean"==typeof h&&(k=h,h=arguments[1]||{},i=2),"object"==typeof h||o.isFunction(h)||(h={}),j===i&&(h=this,--i),i;j>i;i++)if(null!==(a=arguments[i]))for(b in a)c=h[b],d=a[b],h!==d&&(k&&d&&(o.isPlainObject(d)||(e=o.isArray(d)))?(e?(e=!1,f=c&&o.isArray(c)?c:[]):f=c&&o.isPlainObject(c)?c:{},h[b]=g(k,f,d)):void 0!==d&&(h[b]=d));return h},h={toRadians:function(a){return a*(Math.PI/180)},toDegrees:function(a){return a*(180/Math.PI)},computePieRadius:function(a){var b=a.options.size,c=a.options.misc.canvasPadding,d=b.canvasWidth-c.left-c.right,e=b.canvasHeight-c.top-c.bottom;"pie-center"!==a.options.header.location&&(e-=a.textComponents.headerHeight),a.textComponents.footer.exists&&(e-=a.textComponents.footer.h),e=0>e?0:e;var f,g,h=(e>d?d:e)/3;if(null!==b.pieOuterRadius)if(/%/.test(b.pieOuterRadius)){g=parseInt(b.pieOuterRadius.replace(/[\D]/,""),10),g=g>99?99:g,g=0>g?0:g;var i=e>d?d:e;if("none"!==a.options.labels.outer.format){var j=2*parseInt(a.options.labels.outer.pieDistance,10);i-j>0&&(i-=j)}h=Math.floor(i/100*g)/2}else h=parseInt(b.pieOuterRadius,10);/%/.test(b.pieInnerRadius)?(g=parseInt(b.pieInnerRadius.replace(/[\D]/,""),10),g=g>99?99:g,g=0>g?0:g,f=Math.floor(h/100*g)):f=parseInt(b.pieInnerRadius,10),a.innerRadius=f,a.outerRadius=h},getTotalPieSize:function(a){for(var b=0,c=0;c<a.length;c++)b+=a[c].value;return b},sortPieData:function(a){var b=a.options.data.content,c=a.options.data.sortOrder;switch(c){case"none":break;case"random":b=f.shuffleArray(b);break;case"value-asc":b.sort(function(a,b){return a.value<b.value?-1:1});break;case"value-desc":b.sort(function(a,b){return a.value<b.value?1:-1});break;case"label-asc":b.sort(function(a,b){return a.label.toLowerCase()>b.label.toLowerCase()?1:-1});break;case"label-desc":b.sort(function(a,b){return a.label.toLowerCase()<b.label.toLowerCase()?1:-1})}return b},getPieTranslateCenter:function(a){return"translate("+a.x+","+a.y+")"},calculatePieCenter:function(a){var b=a.options.misc.pieCenterOffset,c=a.textComponents.title.exists&&"pie-center"!==a.options.header.location,d=a.textComponents.subtitle.exists&&"pie-center"!==a.options.header.location,e=a.options.misc.canvasPadding.top;c&&d?e+=a.textComponents.title.h+a.options.header.titleSubtitlePadding+a.textComponents.subtitle.h:c?e+=a.textComponents.title.h:d&&(e+=a.textComponents.subtitle.h);var f=0;a.textComponents.footer.exists&&(f=a.textComponents.footer.h+a.options.misc.canvasPadding.bottom);var g=(a.options.size.canvasWidth-a.options.misc.canvasPadding.left-a.options.misc.canvasPadding.right)/2+a.options.misc.canvasPadding.left,h=(a.options.size.canvasHeight-f-e)/2+e;g+=b.x,h+=b.y,a.pieCenter={x:g,y:h}},rotate:function(a,b,c,d,e){e=e*Math.PI/180;var f=Math.cos,g=Math.sin,h=(a-c)*f(e)-(b-d)*g(e)+c,i=(a-c)*g(e)+(b-d)*f(e)+d;return{x:h,y:i}},translate:function(a,b,c,d){var e=h.toRadians(d);return{x:a+c*Math.sin(e),y:b-c*Math.cos(e)}},pointIsInArc:function(a,b,c){var d=c.innerRadius()(b),e=c.outerRadius()(b),f=c.startAngle()(b),g=c.endAngle()(b),h=a.x*a.x+a.y*a.y,i=Math.atan2(a.x,-a.y);return i=0>i?i+2*Math.PI:i,h>=d*d&&e*e>=h&&i>=f&&g>=i}},i={add:function(a,b,c){var d=i.getIncludes(c),e=a.options.labels,f=a.svg.insert("g","."+a.cssPrefix+"labels-"+b).attr("class",a.cssPrefix+"labels-"+b),g=f.selectAll("."+a.cssPrefix+"labelGroup-"+b).data(a.options.data.content).enter().append("g").attr("id",function(c,d){return a.cssPrefix+"labelGroup"+d+"-"+b}).attr("data-index",function(a,b){return b}).attr("class",a.cssPrefix+"labelGroup-"+b).style("opacity",0),h={section:b,sectionDisplayType:c};d.mainLabel&&g.append("text").attr("id",function(c,d){return a.cssPrefix+"segmentMainLabel"+d+"-"+b}).attr("class",a.cssPrefix+"segmentMainLabel-"+b).text(function(a,b){var c=a.label;return e.formatter?(h.index=b,h.part="mainLabel",h.value=a.value,h.label=c,c=e.formatter(h)):e.truncation.enabled&&a.label.length>e.truncation.truncateLength&&(c=a.label.substring(0,e.truncation.truncateLength)+"..."),c}).style("font-size",e.mainLabel.fontSize+"px").style("font-family",e.mainLabel.font).style("fill",e.mainLabel.color),d.percentage&&g.append("text").attr("id",function(c,d){return a.cssPrefix+"segmentPercentage"+d+"-"+b}).attr("class",a.cssPrefix+"segmentPercentage-"+b).text(function(b,c){var d=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces);return e.formatter?(h.index=c,h.part="percentage",h.value=b.value,h.label=d,d=e.formatter(h)):d+="%",d}).style("font-size",e.percentage.fontSize+"px").style("font-family",e.percentage.font).style("fill",e.percentage.color),d.value&&g.append("text").attr("id",function(c,d){return a.cssPrefix+"segmentValue"+d+"-"+b}).attr("class",a.cssPrefix+"segmentValue-"+b).text(function(a,b){return h.index=b,h.part="value",h.value=a.value,h.label=a.value,e.formatter?e.formatter(h,a.value):a.value}).style("font-size",e.value.fontSize+"px").style("font-family",e.value.font).style("fill",e.value.color)},positionLabelElements:function(a,b,c){i["dimensions-"+b]=[];var d=d3.selectAll("."+a.cssPrefix+"labelGroup-"+b);d.each(function(c,d){var e=d3.select(this).selectAll("."+a.cssPrefix+"segmentMainLabel-"+b),f=d3.select(this).selectAll("."+a.cssPrefix+"segmentPercentage-"+b),g=d3.select(this).selectAll("."+a.cssPrefix+"segmentValue-"+b);i["dimensions-"+b].push({mainLabel:null!==e.node()?e.node().getBBox():null,percentage:null!==f.node()?f.node().getBBox():null,value:null!==g.node()?g.node().getBBox():null})});var e=5,f=i["dimensions-"+b];switch(c){case"label-value1":d3.selectAll("."+a.cssPrefix+"segmentValue-"+b).attr("dx",function(a,b){return f[b].mainLabel.width+e});break;case"label-value2":d3.selectAll("."+a.cssPrefix+"segmentValue-"+b).attr("dy",function(a,b){return f[b].mainLabel.height});break;case"label-percentage1":d3.selectAll("."+a.cssPrefix+"segmentPercentage-"+b).attr("dx",function(a,b){return f[b].mainLabel.width+e});break;case"label-percentage2":d3.selectAll("."+a.cssPrefix+"segmentPercentage-"+b).attr("dx",function(a,b){return f[b].mainLabel.width/2-f[b].percentage.width/2}).attr("dy",function(a,b){return f[b].mainLabel.height})}},computeLabelLinePositions:function(a){a.lineCoordGroups=[],d3.selectAll("."+a.cssPrefix+"labelGroup-outer").each(function(b,c){return i.computeLinePosition(a,c)})},computeLinePosition:function(a,b){var c,d,e,f,g=j.getSegmentAngle(b,a.options.data.content,a.totalSize,{midpoint:!0}),i=h.rotate(a.pieCenter.x,a.pieCenter.y-a.outerRadius,a.pieCenter.x,a.pieCenter.y,g),k=a.outerLabelGroupData[b].h/5,l=6,m=Math.floor(g/90),n=4;switch(2===m&&180===g&&(m=1),m){case 0:c=a.outerLabelGroupData[b].x-l-(a.outerLabelGroupData[b].x-l-i.x)/2,d=a.outerLabelGroupData[b].y+(i.y-a.outerLabelGroupData[b].y)/n,e=a.outerLabelGroupData[b].x-l,f=a.outerLabelGroupData[b].y-k;break;case 1:c=i.x+(a.outerLabelGroupData[b].x-i.x)/n,d=i.y+(a.outerLabelGroupData[b].y-i.y)/n,e=a.outerLabelGroupData[b].x-l,f=a.outerLabelGroupData[b].y-k;break;case 2:var o=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l;c=i.x-(i.x-o)/n,d=i.y+(a.outerLabelGroupData[b].y-i.y)/n,e=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l,f=a.outerLabelGroupData[b].y-k;break;case 3:var p=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l;c=p+(i.x-p)/n,d=a.outerLabelGroupData[b].y+(i.y-a.outerLabelGroupData[b].y)/n,e=a.outerLabelGroupData[b].x+a.outerLabelGroupData[b].w+l,f=a.outerLabelGroupData[b].y-k}"straight"===a.options.labels.lines.style?a.lineCoordGroups[b]=[{x:i.x,y:i.y},{x:e,y:f}]:a.lineCoordGroups[b]=[{x:i.x,y:i.y},{x:c,y:d},{x:e,y:f}]},addLabelLines:function(a){var b=a.svg.insert("g","."+a.cssPrefix+"pieChart").attr("class",a.cssPrefix+"lineGroups").style("opacity",0),c=b.selectAll("."+a.cssPrefix+"lineGroup").data(a.lineCoordGroups).enter().append("g").attr("class",a.cssPrefix+"lineGroup"),d=d3.svg.line().interpolate("basis").x(function(a){return a.x}).y(function(a){return a.y});c.append("path").attr("d",d).attr("stroke",function(b,c){return"segment"===a.options.labels.lines.color?a.options.colors[c]:a.options.labels.lines.color}).attr("stroke-width",1).attr("fill","none").style("opacity",function(b,c){var d=a.options.labels.outer.hideWhenLessThanPercentage,e=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces),f=null!==d&&d>e||""===a.options.data.content[c].label;return f?0:1})},positionLabelGroups:function(a,b){"none"!==a.options.labels[b].format&&d3.selectAll("."+a.cssPrefix+"labelGroup-"+b).style("opacity",0).attr("transform",function(c,d){var e,i;if("outer"===b)e=a.outerLabelGroupData[d].x,i=a.outerLabelGroupData[d].y;else{var k=g(!0,{},a.pieCenter);if(a.innerRadius>0){var l=j.getSegmentAngle(d,a.options.data.content,a.totalSize,{midpoint:!0}),m=h.translate(a.pieCenter.x,a.pieCenter.y,a.innerRadius,l);k.x=m.x,k.y=m.y}var n=f.getDimensions(a.cssPrefix+"labelGroup"+d+"-inner"),o=n.w/2,p=n.h/4;e=k.x+(a.lineCoordGroups[d][0].x-k.x)/1.8,i=k.y+(a.lineCoordGroups[d][0].y-k.y)/1.8,e-=o,i+=p}return"translate("+e+","+i+")"})},fadeInLabelsAndLines:function(a){var b="default"===a.options.effects.load.effect?a.options.effects.load.speed:1;setTimeout(function(){var b="default"===a.options.effects.load.effect?400:1;d3.selectAll("."+a.cssPrefix+"labelGroup-outer").transition().duration(b).style("opacity",function(b,c){var d=a.options.labels.outer.hideWhenLessThanPercentage,e=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces);return null!==d&&d>e?0:1}),d3.selectAll("."+a.cssPrefix+"labelGroup-inner").transition().duration(b).style("opacity",function(b,c){var d=a.options.labels.inner.hideWhenLessThanPercentage,e=j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces);return null!==d&&d>e?0:1}),d3.selectAll("g."+a.cssPrefix+"lineGroups").transition().duration(b).style("opacity",1),f.isFunction(a.options.callbacks.onload)&&setTimeout(function(){try{a.options.callbacks.onload()}catch(b){}},b)},b)},getIncludes:function(a){var b=!1,c=!1,d=!1;switch(a){case"label":b=!0;break;case"value":c=!0;break;case"percentage":d=!0;break;case"label-value1":case"label-value2":b=!0,c=!0;break;case"label-percentage1":case"label-percentage2":b=!0,d=!0}return{mainLabel:b,value:c,percentage:d}},computeOuterLabelCoords:function(a){a.svg.selectAll("."+a.cssPrefix+"labelGroup-outer").each(function(b,c){return i.getIdealOuterLabelPositions(a,c)}),i.resolveOuterLabelCollisions(a)},resolveOuterLabelCollisions:function(a){if("none"!==a.options.labels.outer.format){var b=a.options.data.content.length;i.checkConflict(a,0,"clockwise",b),i.checkConflict(a,b-1,"anticlockwise",b)}},checkConflict:function(a,b,c,d){var e,g;if(!(1>=d)){var h=a.outerLabelGroupData[b].hs;if(!("clockwise"===c&&"right"!==h||"anticlockwise"===c&&"left"!==h)){var j="clockwise"===c?b+1:b-1,k=a.outerLabelGroupData[b],l=a.outerLabelGroupData[j],m={labelHeights:a.outerLabelGroupData[0].h,center:a.pieCenter,lineLength:a.outerRadius+a.options.labels.outer.pieDistance,heightChange:a.outerLabelGroupData[0].h+1};if("clockwise"===c){for(e=0;b>=e;e++)if(g=a.outerLabelGroupData[e],!i.isLabelHidden(a,e)&&f.rectIntersect(g,l)){i.adjustLabelPos(a,j,k,m);break}}else for(e=d-1;e>=b;e--)if(g=a.outerLabelGroupData[e],!i.isLabelHidden(a,e)&&f.rectIntersect(g,l)){i.adjustLabelPos(a,j,k,m);break}i.checkConflict(a,j,c,d)}}},isLabelHidden:function(a,b){var c=a.options.labels.outer.hideWhenLessThanPercentage,d=j.getPercentage(a,b,a.options.labels.percentage.decimalPlaces);return null!==c&&c>d||""===a.options.data.content[b].label},adjustLabelPos:function(a,b,c,d){var e,f,g,h;h=c.y+d.heightChange,f=d.center.y-h,e=Math.sqrt(Math.abs(d.lineLength)>Math.abs(f)?d.lineLength*d.lineLength-f*f:f*f-d.lineLength*d.lineLength),g="right"===c.hs?d.center.x+e:d.center.x-e-a.outerLabelGroupData[b].w,a.outerLabelGroupData[b].x=g,a.outerLabelGroupData[b].y=h},getIdealOuterLabelPositions:function(a,b){var c=d3.select("#"+a.cssPrefix+"labelGroup"+b+"-outer").node();if(c){var d=c.getBBox(),e=j.getSegmentAngle(b,a.options.data.content,a.totalSize,{midpoint:!0}),f=a.pieCenter.x,g=a.pieCenter.y-(a.outerRadius+a.options.labels.outer.pieDistance),i=h.rotate(f,g,a.pieCenter.x,a.pieCenter.y,e),k="right";e>180?(i.x-=d.width+8,k="left"):i.x+=8,a.outerLabelGroupData[b]={x:i.x,y:i.y,w:d.width,h:d.height,hs:k}}}},j={create:function(a){var b=a.pieCenter,c=a.options.colors,d=a.options.effects.load,e=a.options.misc.colors.segmentStroke,f=a.svg.insert("g","#"+a.cssPrefix+"title").attr("transform",function(){return h.getPieTranslateCenter(b)}).attr("class",a.cssPrefix+"pieChart"),g=d3.svg.arc().innerRadius(a.innerRadius).outerRadius(a.outerRadius).startAngle(0).endAngle(function(b){return b.value/a.totalSize*2*Math.PI}),i=f.selectAll("."+a.cssPrefix+"arc").data(a.options.data.content).enter().append("g").attr("class",a.cssPrefix+"arc"),k=d.speed;"none"===d.effect&&(k=0),i.append("path").attr("id",function(b,c){return a.cssPrefix+"segment"+c}).attr("fill",function(b,d){var e=c[d];return a.options.misc.gradient.enabled&&(e="url(#"+a.cssPrefix+"grad"+d+")"),e}).style("stroke",e).style("stroke-width",1).transition().ease("cubic-in-out").duration(k).attr("data-index",function(a,b){return b}).attrTween("d",function(b){var c=d3.interpolate({value:0},b);return function(b){return a.arc(c(b))}}),a.svg.selectAll("g."+a.cssPrefix+"arc").attr("transform",function(b,c){var d=0;return c>0&&(d=j.getSegmentAngle(c-1,a.options.data.content,a.totalSize)),"rotate("+d+")"}),a.arc=g},addGradients:function(a){var b=a.svg.append("defs").selectAll("radialGradient").data(a.options.data.content).enter().append("radialGradient").attr("gradientUnits","userSpaceOnUse").attr("cx",0).attr("cy",0).attr("r","120%").attr("id",function(b,c){return a.cssPrefix+"grad"+c});b.append("stop").attr("offset","0%").style("stop-color",function(b,c){return a.options.colors[c]}),b.append("stop").attr("offset",a.options.misc.gradient.percentage+"%").style("stop-color",a.options.misc.gradient.color)},addSegmentEventHandlers:function(a){var b=d3.selectAll("."+a.cssPrefix+"arc,."+a.cssPrefix+"labelGroup-inner,."+a.cssPrefix+"labelGroup-outer");b.on("click",function(){var b,c=d3.select(this);if(c.attr("class")===a.cssPrefix+"arc")b=c.select("path");else{var d=c.attr("data-index");b=d3.select("#"+a.cssPrefix+"segment"+d)}var e=b.attr("class")===a.cssPrefix+"expanded";j.onSegmentEvent(a,a.options.callbacks.onClickSegment,b,e),"none"!==a.options.effects.pullOutSegmentOnClick.effect&&(e?j.closeSegment(a,b.node()):j.openSegment(a,b.node()))}),b.on("mouseover",function(){var b,c,d=d3.select(this);if(d.attr("class")===a.cssPrefix+"arc"?b=d.select("path"):(c=d.attr("data-index"),b=d3.select("#"+a.cssPrefix+"segment"+c)),a.options.effects.highlightSegmentOnMouseover){c=b.attr("data-index");var e=a.options.colors[c];b.style("fill",f.getColorShade(e,a.options.effects.highlightLuminosity))}a.options.tooltips.enabled&&(c=b.attr("data-index"),l.showTooltip(a,c));var g=b.attr("class")===a.cssPrefix+"expanded";j.onSegmentEvent(a,a.options.callbacks.onMouseoverSegment,b,g)}),b.on("mousemove",function(){l.moveTooltip(a)}),b.on("mouseout",function(){var b,c,d=d3.select(this);if(d.attr("class")===a.cssPrefix+"arc"?b=d.select("path"):(c=d.attr("data-index"),b=d3.select("#"+a.cssPrefix+"segment"+c)),a.options.effects.highlightSegmentOnMouseover){c=b.attr("data-index");var e=a.options.colors[c];a.options.misc.gradient.enabled&&(e="url(#"+a.cssPrefix+"grad"+c+")"),b.style("fill",e)}a.options.tooltips.enabled&&(c=b.attr("data-index"),l.hideTooltip(a,c));var f=b.attr("class")===a.cssPrefix+"expanded";j.onSegmentEvent(a,a.options.callbacks.onMouseoutSegment,b,f)})},onSegmentEvent:function(a,b,c,d){if(f.isFunction(b)){var e=parseInt(c.attr("data-index"),10);b({segment:c.node(),index:e,expanded:d,data:a.options.data.content[e]})}},openSegment:function(a,b){a.isOpeningSegment||(a.isOpeningSegment=!0,d3.selectAll("."+a.cssPrefix+"expanded").length>0&&j.closeSegment(a,d3.select("."+a.cssPrefix+"expanded").node()),d3.select(b).transition().ease(a.options.effects.pullOutSegmentOnClick.effect).duration(a.options.effects.pullOutSegmentOnClick.speed).attr("transform",function(b,c){var d=a.arc.centroid(b),e=d[0],f=d[1],g=Math.sqrt(e*e+f*f),h=parseInt(a.options.effects.pullOutSegmentOnClick.size,10);return"translate("+e/g*h+","+f/g*h+")"}).each("end",function(c,d){a.currentlyOpenSegment=b,a.isOpeningSegment=!1,d3.select(this).attr("class",a.cssPrefix+"expanded")}))},closeSegment:function(a,b){d3.select(b).transition().duration(400).attr("transform","translate(0,0)").each("end",function(b,c){d3.select(this).attr("class",""),a.currentlyOpenSegment=null})},getCentroid:function(a){var b=a.getBBox();return{x:b.x+b.width/2,y:b.y+b.height/2}},getSegmentAngle:function(a,b,c,d){var e,f=g({compounded:!0,midpoint:!1},d),h=b[a].value;if(f.compounded){e=0;for(var i=0;a>=i;i++)e+=b[i].value}"undefined"==typeof e&&(e=h);var j=e/c*360;if(f.midpoint){var k=h/c*360;j-=k/2}return j},getPercentage:function(a,b,c){var d=a.options.data.content[b].value/a.totalSize;return 0>=c?Math.round(100*d):(100*d).toFixed(c)}},k={offscreenCoord:-1e4,addTitle:function(a){a.svg.selectAll("."+a.cssPrefix+"title").data([a.options.header.title]).enter().append("text").text(function(a){return a.text}).attr({id:a.cssPrefix+"title","class":a.cssPrefix+"title",x:k.offscreenCoord,y:k.offscreenCoord}).attr("text-anchor",function(){var b;return b="top-center"===a.options.header.location||"pie-center"===a.options.header.location?"middle":"left"}).attr("fill",function(a){return a.color}).style("font-size",function(a){return a.fontSize+"px"}).style("font-family",function(a){return a.font})},positionTitle:function(a){var b,c=a.textComponents,d=a.options.header.location,e=a.options.misc.canvasPadding,f=a.options.size.canvasWidth,g=a.options.header.titleSubtitlePadding;b="top-left"===d?e.left:(f-e.right)/2+e.left,b+=a.options.misc.pieCenterOffset.x;var h=e.top+c.title.h;if("pie-center"===d)if(h=a.pieCenter.y,c.subtitle.exists){var i=c.title.h+g+c.subtitle.h;h=h-i/2+c.title.h}else h+=c.title.h/4;a.svg.select("#"+a.cssPrefix+"title").attr("x",b).attr("y",h)},addSubtitle:function(a){var b=a.options.header.location;a.svg.selectAll("."+a.cssPrefix+"subtitle").data([a.options.header.subtitle]).enter().append("text").text(function(a){return a.text}).attr("x",k.offscreenCoord).attr("y",k.offscreenCoord).attr("id",a.cssPrefix+"subtitle").attr("class",a.cssPrefix+"subtitle").attr("text-anchor",function(){var a;return a="top-center"===b||"pie-center"===b?"middle":"left"}).attr("fill",function(a){return a.color}).style("font-size",function(a){return a.fontSize+"px"}).style("font-family",function(a){return a.font})},positionSubtitle:function(a){var b,c=a.options.misc.canvasPadding,d=a.options.size.canvasWidth;b="top-left"===a.options.header.location?c.left:(d-c.right)/2+c.left,b+=a.options.misc.pieCenterOffset.x;var e=k.getHeaderHeight(a);a.svg.select("#"+a.cssPrefix+"subtitle").attr("x",b).attr("y",e)},addFooter:function(a){a.svg.selectAll("."+a.cssPrefix+"footer").data([a.options.footer]).enter().append("text").text(function(a){return a.text}).attr("x",k.offscreenCoord).attr("y",k.offscreenCoord).attr("id",a.cssPrefix+"footer").attr("class",a.cssPrefix+"footer").attr("text-anchor",function(){var b="left";return"bottom-center"===a.options.footer.location?b="middle":"bottom-right"===a.options.footer.location&&(b="left"),b}).attr("fill",function(a){return a.color}).style("font-size",function(a){return a.fontSize+"px"}).style("font-family",function(a){return a.font})},positionFooter:function(a){var b,c=a.options.footer.location,d=a.textComponents.footer.w,e=a.options.size.canvasWidth,f=a.options.size.canvasHeight,g=a.options.misc.canvasPadding;b="bottom-left"===c?g.left:"bottom-right"===c?e-d-g.right:e/2,a.svg.select("#"+a.cssPrefix+"footer").attr("x",b).attr("y",f-g.bottom)},getHeaderHeight:function(a){var b;if(a.textComponents.title.exists){var c=a.textComponents.title.h+a.options.header.titleSubtitlePadding+a.textComponents.subtitle.h;b="pie-center"===a.options.header.location?a.pieCenter.y-c/2+c:c+a.options.misc.canvasPadding.top}else if("pie-center"===a.options.header.location){var d=a.options.misc.canvasPadding.bottom+a.textComponents.footer.h;b=(a.options.size.canvasHeight-d)/2+a.options.misc.canvasPadding.top+a.textComponents.subtitle.h/2}else b=a.options.misc.canvasPadding.top+a.textComponents.subtitle.h;return b}},l={addTooltips:function(a){var b=a.svg.insert("g").attr("class",a.cssPrefix+"tooltips");b.selectAll("."+a.cssPrefix+"tooltip").data(a.options.data.content).enter().append("g").attr("class",a.cssPrefix+"tooltip").attr("id",function(b,c){return a.cssPrefix+"tooltip"+c}).style("opacity",0).append("rect").attr({rx:a.options.tooltips.styles.borderRadius,ry:a.options.tooltips.styles.borderRadius,x:-a.options.tooltips.styles.padding,opacity:a.options.tooltips.styles.backgroundOpacity}).style("fill",a.options.tooltips.styles.backgroundColor),b.selectAll("."+a.cssPrefix+"tooltip").data(a.options.data.content).append("text").attr("fill",function(b){return a.options.tooltips.styles.color}).style("font-size",function(b){return a.options.tooltips.styles.fontSize}).style("font-family",function(b){return a.options.tooltips.styles.font}).text(function(b,c){var d=a.options.tooltips.string;return"caption"===a.options.tooltips.type&&(d=b.caption),l.replacePlaceholders(a,d,c,{label:b.label,value:b.value,percentage:j.getPercentage(a,c,a.options.labels.percentage.decimalPlaces)})}),b.selectAll("."+a.cssPrefix+"tooltip rect").attr({width:function(b,c){var d=f.getDimensions(a.cssPrefix+"tooltip"+c);return d.w+2*a.options.tooltips.styles.padding},height:function(b,c){var d=f.getDimensions(a.cssPrefix+"tooltip"+c);return d.h+2*a.options.tooltips.styles.padding},y:function(b,c){var d=f.getDimensions(a.cssPrefix+"tooltip"+c);return-(d.h/2)+1}})},showTooltip:function(a,b){var c=a.options.tooltips.styles.fadeInSpeed;l.currentTooltip===b&&(c=1),l.currentTooltip=b,d3.select("#"+a.cssPrefix+"tooltip"+b).transition().duration(c).style("opacity",function(){return 1}),l.moveTooltip(a)},moveTooltip:function(a){d3.selectAll("#"+a.cssPrefix+"tooltip"+l.currentTooltip).attr("transform",function(b){var c=d3.mouse(this.parentNode),d=c[0]+a.options.tooltips.styles.padding+2,e=c[1]-2*a.options.tooltips.styles.padding-2;return"translate("+d+","+e+")"})},hideTooltip:function(a,b){d3.select("#"+a.cssPrefix+"tooltip"+b).style("opacity",function(){return 0}),d3.select("#"+a.cssPrefix+"tooltip"+l.currentTooltip).attr("transform",function(b,c){var d=a.options.size.canvasWidth+1e3,e=a.options.size.canvasHeight+1e3;return"translate("+d+","+e+")"})},replacePlaceholders:function(a,b,c,d){f.isFunction(a.options.tooltips.placeholderParser)&&a.options.tooltips.placeholderParser(c,d);var e=function(){return function(a){var b=arguments[1];return d.hasOwnProperty(b)?d[arguments[1]]:arguments[0]}};return b.replace(/\{(\w+)\}/g,e(d))}},m=function(i,j){if(this.element=i,"string"==typeof i){var k=i.replace(/^#/,"");this.element=document.getElementById(k)}var l={};g(!0,l,d,j),this.options=l,null!==this.options.misc.cssPrefix?this.cssPrefix=this.options.misc.cssPrefix:(this.cssPrefix="p"+c+"_",c++),e.initialCheck(this)&&(d3.select(this.element).attr(a,b),this.options.data.content=h.sortPieData(this),this.options.data.smallSegmentGrouping.enabled&&(this.options.data.content=f.applySmallSegmentGrouping(this.options.data.content,this.options.data.smallSegmentGrouping)),this.options.colors=f.initSegmentColors(this),this.totalSize=h.getTotalPieSize(this.options.data.content),n.call(this))};m.prototype.recreate=function(){e.initialCheck(this)&&(this.options.data.content=h.sortPieData(this),this.options.data.smallSegmentGrouping.enabled&&(this.options.data.content=f.applySmallSegmentGrouping(this.options.data.content,this.options.data.smallSegmentGrouping)),this.options.colors=f.initSegmentColors(this),this.totalSize=h.getTotalPieSize(this.options.data.content),n.call(this))},m.prototype.redraw=function(){this.element.innerHTML="",n.call(this)},m.prototype.destroy=function(){this.element.innerHTML="",d3.select(this.element).attr(a,null)},m.prototype.getOpenSegment=function(){var a=this.currentlyOpenSegment;if(null!==a&&"undefined"!=typeof a){var b=parseInt(d3.select(a).attr("data-index"),10);return{element:a,index:b,data:this.options.data.content[b]}}return null},m.prototype.openSegment=function(a){a=parseInt(a,10),0>a||a>this.options.data.content.length-1||j.openSegment(this,d3.select("#"+this.cssPrefix+"segment"+a).node())},m.prototype.closeSegment=function(){var a=this.currentlyOpenSegment;a&&j.closeSegment(this,a)},m.prototype.updateProp=function(a,b){switch(a){case"header.title.text":var c=f.processObj(this.options,a);f.processObj(this.options,a,b),d3.select("#"+this.cssPrefix+"title").html(b),(""===c&&""!==b||""!==c&&""===b)&&this.redraw();break;case"header.subtitle.text":var d=f.processObj(this.options,a);f.processObj(this.options,a,b),d3.select("#"+this.cssPrefix+"subtitle").html(b),(""===d&&""!==b||""!==d&&""===b)&&this.redraw();break;case"callbacks.onload":case"callbacks.onMouseoverSegment":case"callbacks.onMouseoutSegment":case"callbacks.onClickSegment":case"effects.pullOutSegmentOnClick.effect":case"effects.pullOutSegmentOnClick.speed":case"effects.pullOutSegmentOnClick.size":case"effects.highlightSegmentOnMouseover":case"effects.highlightLuminosity":f.processObj(this.options,a,b);break;default:f.processObj(this.options,a,b),this.destroy(),this.recreate()}};var n=function(){this.svg=f.addSVGSpace(this),
+this.textComponents={headerHeight:0,title:{exists:""!==this.options.header.title.text,h:0,w:0},subtitle:{exists:""!==this.options.header.subtitle.text,h:0,w:0},footer:{exists:""!==this.options.footer.text,h:0,w:0}},this.outerLabelGroupData=[],this.textComponents.title.exists&&k.addTitle(this),this.textComponents.subtitle.exists&&k.addSubtitle(this),k.addFooter(this);var a=this;f.whenIdExists(this.cssPrefix+"footer",function(){k.positionFooter(a);var b=f.getDimensions(a.cssPrefix+"footer");a.textComponents.footer.h=b.h,a.textComponents.footer.w=b.w});var b=[];this.textComponents.title.exists&&b.push(this.cssPrefix+"title"),this.textComponents.subtitle.exists&&b.push(this.cssPrefix+"subtitle"),this.textComponents.footer.exists&&b.push(this.cssPrefix+"footer"),f.whenElementsExist(b,function(){if(a.textComponents.title.exists){var b=f.getDimensions(a.cssPrefix+"title");a.textComponents.title.h=b.h,a.textComponents.title.w=b.w}if(a.textComponents.subtitle.exists){var c=f.getDimensions(a.cssPrefix+"subtitle");a.textComponents.subtitle.h=c.h,a.textComponents.subtitle.w=c.w}if(a.textComponents.title.exists||a.textComponents.subtitle.exists){var d=0;a.textComponents.title.exists&&(d+=a.textComponents.title.h,a.textComponents.subtitle.exists&&(d+=a.options.header.titleSubtitlePadding)),a.textComponents.subtitle.exists&&(d+=a.textComponents.subtitle.h),a.textComponents.headerHeight=d}h.computePieRadius(a),h.calculatePieCenter(a),k.positionTitle(a),k.positionSubtitle(a),a.options.misc.gradient.enabled&&j.addGradients(a),j.create(a),i.add(a,"inner",a.options.labels.inner.format),i.add(a,"outer",a.options.labels.outer.format),i.positionLabelElements(a,"inner",a.options.labels.inner.format),i.positionLabelElements(a,"outer",a.options.labels.outer.format),i.computeOuterLabelCoords(a),i.positionLabelGroups(a,"outer"),i.computeLabelLinePositions(a),a.options.labels.lines.enabled&&"none"!==a.options.labels.outer.format&&i.addLabelLines(a),i.positionLabelGroups(a,"inner"),i.fadeInLabelsAndLines(a),a.options.tooltips.enabled&&l.addTooltips(a),j.addSegmentEventHandlers(a)})};return m});
\ No newline at end of file
index 2af7b47..ec8ffee 100644 (file)
                        <span class='glyphicon glyphicon-arrow-down'/> Linearization
                </a>
        </li>
+       <li role='presentation'>
+               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics(); entityCtrl.loadPieChart()'>
+                       <span class='glyphicon glyphicon-stats'/> Metrics
+               </a>
+       </li>
 </ul>
 
 <div class='tab-content'>
                        </div>
                </div>
        </div>
+       <div role='tabpanel' class='tab-pane fade' id='metrics'>
+               <div class='card'>
+                       <div class='card-heading'>
+                               <h3 class='card-title'>Class inheritance</h3>
+                       </div>
+                       <div class='card-body container-fluid'>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Inheritance kind
+                                               <small>({{metrics.mclass['cnoa'].values[mentity.full_name].value}}
+                                               ancestors)</small>
+                                       </h4>
+                                       <chart-class-inheritance-kind chart-id='chartInheritanceKind'
+                                               chart-metrics='metrics.mclass' />
+                               </div>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Inheritance metrics
+                                       </h4>
+                                       <dl class='dl-horizontal'>
+                                               <dt>{{metrics.mclass.cnoa.values[mentity.full_name].value}}</dt>
+                                               <dd>ancestors</dd>
+                                               <dt>{{metrics.mclass.cnop.values[mentity.full_name].value}}</dt>
+                                               <dd>direct parents</dd>
+                                               <dt>{{metrics.mclass.cnoc.values[mentity.full_name].value}}</dt>
+                                               <dd>direct children</dd>
+                                               <dt>{{metrics.mclass.cnod.values[mentity.full_name].value}}</dt>
+                                               <dd>descendants</dd>
+                                       </dl>
+                                       <dl class='dl-horizontal'>
+                                               <dt>{{metrics.mclass.cdit.values[mentity.full_name].value}}</dt>
+                                               <dd>Depth in Inheritance Tree</dd>
+                                       </dl>
+                               </div>
+                       </div>
+               </div>
+               <div class='card'>
+                       <div class='card-heading'>
+                               <h3 class='card-title'>Class properties</h3>
+                       </div>
+                       <div class='card-body container-fluid'>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Properties kind
+                                               <small>({{metrics.mclass['cnbp'].values[mentity.full_name].value}}
+                                               accessible properties)</small>
+                                       </h4>
+                                       <chart-class-properties-kind chart-id='chartPropertiesKind'
+                                               chart-metrics='metrics.mclass' />
+                               </div>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Properties inheritance
+                                               <small>({{metrics.mclass['cnbp'].values[mentity.full_name].value}}
+                                               accessible properties)</small>
+                                       </h4>
+                                       <chart-class-properties-inh chart-id='chartPropertiesInh'
+                                               chart-metrics='metrics.mclass' />
+                               </div>
+                       </div>
+               </div>
+       </div>
 </div>
index 0b58963..097ec4a 100644 (file)
@@ -9,6 +9,11 @@
                        <span class='glyphicon glyphicon-object-align-vertical'/> Imports
                </a>
        </li>
+       <li role='presentation'>
+               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics()'>
+                       <span class='glyphicon glyphicon-stats'/> Metrics
+               </a>
+       </li>
 </ul>
 
 <div class='tab-content'>
                        </div>
                </div>
        </div>
+       <div role='tabpanel' class='tab-pane fade' id='metrics'>
+               <metrics-list
+                       list-id='modules_importation'
+                       list-title='Modules importation'
+                       list-metrics='metrics.mmodules'
+                       list-metrics-names='["mdit", "mnoa", "mnop", "mnoc", "mnod"]'
+                       list-metrics-default='"mdit"' />
+               <metrics-list
+                       list-id='modules_definitions'
+                       list-title='Modules content'
+                       list-metrics='metrics.mmodules'
+                       list-metrics-names='["mnbi", "mnbr", "mnbic", "mnbac", "mnbcc"]'
+                       list-metrics-default='"mnbi"' />
+               <metrics-list
+                       list-id='classes_inheritance'
+                       list-title='Classes inheritance'
+                       list-metrics='metrics.mclasses'
+                       list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
+                       list-metrics-default='"cdit"' />
+               <metrics-list
+                       list-id='classes_properties'
+                       list-title='Classes properties'
+                       list-metrics='metrics.mclasses'
+                       list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
+                       list-metrics-default='"cnbp"' />
+       </div>
 </div>
index e80f9cf..29e8130 100644 (file)
                        <span class='glyphicon glyphicon-asterisk'/> Class definitions
                </a>
        </li>
+       <li role='presentation'>
+               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics()'>
+                       <span class='glyphicon glyphicon-stats'/> Metrics
+               </a>
+       </li>
 </ul>
 
 <div class='tab-content'>
                        </div>
                </div>
        </div>
+       <div role='tabpanel' class='tab-pane fade' id='metrics'>
+               <div class='card'>
+                       <div class='card-heading'>
+                               <h3 class='card-title'>Module importation</h3>
+                       </div>
+                       <div class='card-body container-fluid'>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Importation metrics
+                                       </h4>
+                                       <dl class='dl-horizontal'>
+                                               <dt>{{metrics.mmodule.mnoa.values[mentity.full_name].value}}</dt>
+                                               <dd>ancestors</dd>
+                                               <dt>{{metrics.mmodule.mnop.values[mentity.full_name].value}}</dt>
+                                               <dd>direct parents</dd>
+                                               <dt>{{metrics.mmodule.mnoc.values[mentity.full_name].value}}</dt>
+                                               <dd>direct children</dd>
+                                               <dt>{{metrics.mmodule.mnod.values[mentity.full_name].value}}</dt>
+                                               <dd>descendants</dd>
+                                       </dl>
+                                       <dl class='dl-horizontal'>
+                                               <dt>{{metrics.mmodule.mdit.values[mentity.full_name].value}}</dt>
+                                               <dd>Depth in Inheritance Tree</dd>
+                                       </dl>
+                               </div>
+                       </div>
+               </div>
+               <div class='card'>
+                       <div class='card-heading'>
+                               <h3 class='card-title'>Module definitions</h3>
+                       </div>
+                       <div class='card-body container-fluid'>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Class definition kinds
+                                               <small>({{metrics.mmodule['mnbi'].values[mentity.full_name].value +
+                                                       metrics.mclass['mnbc'].values[mentity.full_name].value}}
+                                               class definitions)</small>
+                                       </h4>
+                                       <chart-module-definitions-kind chart-id='chartDefinitionsKind'
+                                               chart-metrics='metrics.mmodule' />
+                               </div>
+                               <div class='col-sm-6'>
+                                       <h4>
+                                               Class definition inheritance
+                                               <small>({{metrics.mmodule['mnbd'].values[mentity.full_name].value}}
+                                               accessible definitions)</small>
+                                       </h4>
+                                       <chart-module-definitions-inh chart-id='chartDefinitionsInh'
+                                               chart-metrics='metrics.mmodule' />
+                               </div>
+                       </div>
+               </div>
+               <metrics-list
+                       list-id='classes_inheritance'
+                       list-title='Classes inheritance'
+                       list-metrics='metrics.mclasses'
+                       list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
+                       list-metrics-default='"cdit"' />
+               <metrics-list
+                       list-id='classes_properties'
+                       list-title='Classes properties'
+                       list-metrics='metrics.mclasses'
+                       list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
+                       list-metrics-default='"cnbp"' />
+       </div>
 </div>
index 32d9a2f..f1f3055 100644 (file)
@@ -9,6 +9,11 @@
                        <span class='glyphicon glyphicon-object-align-vertical'/> Dependencies
                </a>
        </li>
+       <li role='presentation'>
+               <a data-toggle='tab' role='tab' data-target='#metrics' aria-controls='metrics' ng-click='entityCtrl.loadStructuralMetrics()'>
+                       <span class='glyphicon glyphicon-stats'/> Metrics
+               </a>
+       </li>
 </ul>
 
 <div class='tab-content'>
                        </div>
                </div>
        </div>
+       <div role='tabpanel' class='tab-pane fade' id='metrics'>
+               <metrics-list
+                       list-id='modules_importation'
+                       list-title='Modules importation'
+                       list-metrics='metrics.mmodules'
+                       list-metrics-names='["mdit", "mnoa", "mnop", "mnoc", "mnod"]'
+                       list-metrics-default='"mdit"' />
+               <metrics-list
+                       list-id='modules_definitions'
+                       list-title='Modules content'
+                       list-metrics='metrics.mmodules'
+                       list-metrics-names='["mnbi", "mnbr", "mnbic", "mnbac", "mnbcc"]'
+                       list-metrics-default='"mnbi"' />
+               <metrics-list
+                       list-id='classes_inheritance'
+                       list-title='Classes inheritance'
+                       list-metrics='metrics.mclasses'
+                       list-metrics-names='["cdit", "cnoa", "cnop", "cnoc", "cnod"]'
+                       list-metrics-default='"cdit"' />
+               <metrics-list
+                       list-id='classes_properties'
+                       list-title='Classes properties'
+                       list-metrics='metrics.mclasses'
+                       list-metrics-names='["cnbp", "cnba", "cnbip", "cnbrp", "cnbhp", "cnblp"]'
+                       list-metrics-default='"cnbp"' />
+       </div>
 </div>
index 1adb9c9..0dd65be 100644 (file)
@@ -193,8 +193,8 @@ end
 redef class MModule
        private var callbacks_used_from_java = new ForeignCallbackSet
 
-       # Pure java class source file
-       private var java_file: nullable JavaClassTemplate = null
+       # Java source file extracted from user FFI code with generated structure
+       var java_file: nullable JavaClassTemplate = null
 
        # Set up the templates of the Java implementation class
        private fun ensure_java_files
index 7638758..a9a4a6d 100644 (file)
@@ -22,6 +22,8 @@ import mmodules_metrics
 import mclasses_metrics
 
 redef class ToolContext
+
+       # Inheritance related metrics phase
        var inheritance_metrics_phase: Phase = new InheritanceMetricsPhase(self, null)
 end
 
@@ -35,28 +37,30 @@ private class InheritanceMetricsPhase
                var out = "{toolcontext.opt_dir.value or else "metrics"}/inheritance"
                out.mkdir
 
+               var model = toolcontext.modelbuilder.model
+               var model_view = model.private_view
+
                print toolcontext.format_h1("\n# Inheritance metrics")
 
                var hmetrics = new MetricSet
-               hmetrics.register(new MDUI(mainmodule))
-               hmetrics.register(new MDUIC(mainmodule))
-               hmetrics.register(new MDUII(mainmodule))
-               hmetrics.register(new MIF(mainmodule))
-               hmetrics.register(new MIFC(mainmodule))
-               hmetrics.register(new MIFI(mainmodule))
+               hmetrics.register(new MDUI(mainmodule, model_view))
+               hmetrics.register(new MDUIC(mainmodule, model_view))
+               hmetrics.register(new MDUII(mainmodule, model_view))
+               hmetrics.register(new MIF(mainmodule, model_view))
+               hmetrics.register(new MIFC(mainmodule, model_view))
+               hmetrics.register(new MIFI(mainmodule, model_view))
 
                var cmetrics = new MetricSet
-               cmetrics.register(new CNOAC(mainmodule))
-               cmetrics.register(new CNOPC(mainmodule))
-               cmetrics.register(new CNOCC(mainmodule))
-               cmetrics.register(new CNODC(mainmodule))
-               cmetrics.register(new CNOPI(mainmodule))
-               cmetrics.register(new CNOCI(mainmodule))
-               cmetrics.register(new CNODI(mainmodule))
-               cmetrics.register(new CDITC(mainmodule))
-               cmetrics.register(new CDITI(mainmodule))
+               cmetrics.register(new CNOAC(mainmodule, model_view))
+               cmetrics.register(new CNOPC(mainmodule, model_view))
+               cmetrics.register(new CNOCC(mainmodule, model_view))
+               cmetrics.register(new CNODC(mainmodule, model_view))
+               cmetrics.register(new CNOPI(mainmodule, model_view))
+               cmetrics.register(new CNOCI(mainmodule, model_view))
+               cmetrics.register(new CNODI(mainmodule, model_view))
+               cmetrics.register(new CDITC(mainmodule, model_view))
+               cmetrics.register(new CDITI(mainmodule, model_view))
 
-               var model = toolcontext.modelbuilder.model
                var mmodules = new HashSet[MModule]
                var mclasses = new HashSet[MClass]
                for mpackage in model.mpackages do
@@ -108,9 +112,6 @@ class MDUI
        redef fun name do return "mdui"
        redef fun desc do return "proportion of mclass defined using inheritance (has other parent than Object)"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mmodules) do
                for mmodule in mmodules do
                        var count = 0
@@ -135,9 +136,6 @@ class MDUIC
        redef fun name do return "mduic"
        redef fun desc do return "proportion of class_kind defined using inheritance"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mmodules) do
                for mmodule in mmodules do
                        var count = 0
@@ -166,9 +164,6 @@ class MDUII
        redef fun name do return "mduii"
        redef fun desc do return "proportion of interface_kind defined using inheritance"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mmodules) do
                for mmodule in mmodules do
                        var count = 0
@@ -197,9 +192,6 @@ class MIF
        redef fun name do return "mif"
        redef fun desc do return "proportion of mclass inherited from"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mmodules) do
                for mmodule in mmodules do
                        var count = 0
@@ -224,9 +216,6 @@ class MIFC
        redef fun name do return "mifc"
        redef fun desc do return "proportion of class_kind inherited from"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mmodules) do
                for mmodule in mmodules do
                        var count = 0
@@ -255,9 +244,6 @@ class MIFI
        redef fun name do return "mifi"
        redef fun desc do return "proportion of interface_kind inherited from"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mmodules) do
                for mmodule in mmodules do
                        var count = 0
@@ -286,9 +272,6 @@ class CNOAC
        redef fun name do return "cnoac"
        redef fun desc do return "number of class_kind ancestor"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -312,9 +295,6 @@ class CNOPC
        redef fun name do return "cnopc"
        redef fun desc do return "number of class_kind parent"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -338,9 +318,6 @@ class CNOCC
        redef fun name do return "cnocc"
        redef fun desc do return "number of class_kind children"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -364,9 +341,6 @@ class CNODC
        redef fun name do return "cnodc"
        redef fun desc do return "number of class_kind descendants"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -381,6 +355,29 @@ class CNODC
        end
 end
 
+# MClass metric: Number of Abstract Class Ancestors
+#
+# Count only absrtract classes
+class CNOAA
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnoaa"
+       redef fun desc do return "number of abstract class ancestors"
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       var count = 0
+                       for parent in mclass.in_hierarchy(mainmodule).greaters do
+                               if parent == mclass then continue
+                               if parent.kind == abstract_kind then
+                                       count += 1
+                               end
+                       end
+                       values[mclass] = count
+               end
+       end
+end
+
 # MClass metric: Number of Interface Ancestors
 #
 # Count only interfaces
@@ -390,9 +387,6 @@ class CNOAI
        redef fun name do return "cnoai"
        redef fun desc do return "number of interface_kind ancestor"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -416,9 +410,6 @@ class CNOPI
        redef fun name do return "cnopi"
        redef fun desc do return "number of interface_kind parent"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -442,9 +433,6 @@ class CNOCI
        redef fun name do return "cnoci"
        redef fun desc do return "number of interface_kind children"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -468,9 +456,6 @@ class CNODI
        redef fun name do return "cnodi"
        redef fun desc do return "number of interface_kind descendants"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var count = 0
@@ -494,9 +479,6 @@ class CDITC
        redef fun name do return "cditc"
        redef fun desc do return "depth in class tree following only class, abstract, extern kind"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.ditc(mainmodule)
@@ -513,9 +495,6 @@ class CDITI
        redef fun name do return "cditi"
        redef fun desc do return "depth in class tree following only interface_kind"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.diti(mainmodule)
@@ -526,7 +505,7 @@ end
 # model redef
 
 redef class MClass
-               
+
        # Class Depth in Inheritance Tree
        #
        # Following the longest path composed only of extends edges from self to Object
@@ -565,4 +544,3 @@ redef class MClass
                return min
        end
 end
-
index 0951d10..872d66e 100644 (file)
@@ -21,6 +21,8 @@ import metrics_base
 import model::model_collect
 
 redef class ToolContext
+
+       # MClass related metrics phase
        var mclasses_metrics_phase: Phase = new MClassesMetricsPhase(self, null)
 end
 
@@ -40,19 +42,19 @@ private class MClassesMetricsPhase
                print toolcontext.format_h1("\n# MClasses metrics")
 
                var metrics = new MetricSet
-               metrics.register(new CNOA(mainmodule))
-               metrics.register(new CNOP(mainmodule))
-               metrics.register(new CNOC(mainmodule))
-               metrics.register(new CNOD(mainmodule))
-               metrics.register(new CDIT(mainmodule))
+               metrics.register(new CNOA(mainmodule, model_view))
+               metrics.register(new CNOP(mainmodule, model_view))
+               metrics.register(new CNOC(mainmodule, model_view))
+               metrics.register(new CNOD(mainmodule, model_view))
+               metrics.register(new CDIT(mainmodule, model_view))
                metrics.register(new CNBP(mainmodule, model_view))
                metrics.register(new CNBA(mainmodule, model_view))
+               metrics.register(new CNBI(mainmodule, model_view))
+               metrics.register(new CNBM(mainmodule, model_view))
+               metrics.register(new CNBV(mainmodule, model_view))
                metrics.register(new CNBIP(mainmodule, model_view))
                metrics.register(new CNBRP(mainmodule, model_view))
                metrics.register(new CNBHP(mainmodule, model_view))
-               #TODO metrics.register(new CNBI) # nb init
-               #TODO metrics.register(new CNBM) # nb methods
-               #TODO metrics.register(new CNBV) # nb vtypes
 
                var mclasses = new HashSet[MClass]
                for mpackage in model.mpackages do
@@ -86,9 +88,15 @@ private class MClassesMetricsPhase
 end
 
 # A metric about MClass
-interface MClassMetric
+abstract class MClassMetric
        super Metric
        redef type ELM: MClass
+
+       # Main module used for class linearization
+       var mainmodule: MModule
+
+       # Model view used to collect and filter entities
+       var model_view: ModelView
 end
 
 # Class Metric: Number of Ancestors
@@ -98,9 +106,6 @@ class CNOA
        redef fun name do return "cnoa"
        redef fun desc do return "number of ancestor classes"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.in_hierarchy(mainmodule).greaters.length - 1
@@ -115,9 +120,6 @@ class CNOP
        redef fun name do return "cnop"
        redef fun desc do return "number of parent classes"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.in_hierarchy(mainmodule).direct_greaters.length
@@ -132,9 +134,6 @@ class CNOC
        redef fun name do return "cnoc"
        redef fun desc do return "number of child classes"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.in_hierarchy(mainmodule).direct_smallers.length
@@ -149,9 +148,6 @@ class CNOD
        redef fun name do return "cnod"
        redef fun desc do return "number of descendant classes"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.in_hierarchy(mainmodule).smallers.length - 1
@@ -166,9 +162,6 @@ class CDIT
        redef fun name do return "cdit"
        redef fun desc do return "depth in class tree"
 
-       var mainmodule: MModule
-       init(mainmodule: MModule) do self.mainmodule = mainmodule
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.in_hierarchy(mainmodule).depth
@@ -183,14 +176,6 @@ class CNBP
        redef fun name do return "cnbp"
        redef fun desc do return "number of accessible properties (inherited + local)"
 
-       var mainmodule: MModule
-       var model_view: ModelView
-
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
-       end
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.collect_accessible_mproperties(model_view).length
@@ -205,17 +190,51 @@ class CNBA
        redef fun name do return "cnba"
        redef fun desc do return "number of accessible attributes (inherited + local)"
 
-       var mainmodule: MModule
-       var model_view: ModelView
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_accessible_mattributes(model_view).length
+               end
+       end
+end
+
+# Class Metric: Number of MMethods
+class CNBM
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnbm"
+       redef fun desc do return "number of accessible methods (inherited + local)"
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_accessible_mmethods(model_view).length
+               end
+       end
+end
 
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
+# Class Metric: Number of Constructors
+class CNBI
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnbi"
+       redef fun desc do return "number of accessible constructors (inherited + local)"
+
+       redef fun collect(mclasses) do
+               for mclass in mclasses do
+                       values[mclass] = mclass.collect_accessible_inits(model_view).length
+               end
        end
+end
+
+# Class Metric: Number of Virtual Types
+class CNBV
+       super MClassMetric
+       super IntMetric
+       redef fun name do return "cnbv"
+       redef fun desc do return "number of accessible virtual types (inherited + local)"
 
        redef fun collect(mclasses) do
                for mclass in mclasses do
-                       values[mclass] = mclass.collect_accessible_mattributes(model_view).length
+                       values[mclass] = mclass.collect_accessible_vts(model_view).length
                end
        end
 end
@@ -227,14 +246,6 @@ class CNBIP
        redef fun name do return "cnbip"
        redef fun desc do return "number of introduced properties"
 
-       var mainmodule: MModule
-       var model_view: ModelView
-
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
-       end
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.collect_intro_mproperties(model_view).length
@@ -249,14 +260,6 @@ class CNBRP
        redef fun name do return "cnbrp"
        redef fun desc do return "number of redefined properties"
 
-       var mainmodule: MModule
-       var model_view: ModelView
-
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
-       end
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.collect_redef_mproperties(model_view).length
@@ -271,14 +274,6 @@ class CNBHP
        redef fun name do return "cnbhp"
        redef fun desc do return "number of inherited properties"
 
-       var mainmodule: MModule
-       var model_view: ModelView
-
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
-       end
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.collect_inherited_mproperties(model_view).length
@@ -293,14 +288,6 @@ class CNBLP
        redef fun name do return "cnblp"
        redef fun desc do return "number of local properties (intro + redef)"
 
-       var mainmodule: MModule
-       var model_view: ModelView
-
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
-       end
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        values[mclass] = mclass.collect_local_mproperties(model_view).length
index 1c4b6f8..1eb4e5b 100644 (file)
@@ -76,8 +76,8 @@ private class MendelMetricsPhase
                end
 
                var cnblp = new CNBLP(mainmodule, model_view)
-               var cnvi = new CNVI(mainmodule)
-               var cnvs = new CNVS(mainmodule)
+               var cnvi = new CNVI(mainmodule, model_view)
+               var cnvs = new CNVS(mainmodule, model_view)
 
                var metrics = new MetricSet
                metrics.register(cnblp, cnvi, cnvs)
@@ -137,13 +137,9 @@ class CBMS
        redef fun name do return "cbms"
        redef fun desc do return "branch mean size, mean number of introduction available among ancestors"
 
-       # Mainmodule used to compute class hierarchy.
-       var mainmodule: MModule
-       private var protected_view: ModelView = mainmodule.model.protected_view is lateinit
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
-                       var totc = mclass.collect_accessible_mproperties(protected_view).length
+                       var totc = mclass.collect_accessible_mproperties(model_view).length
                        var ditc = mclass.in_hierarchy(mainmodule).depth
                        values[mclass] = totc.to_f / (ditc + 1).to_f
                end
@@ -160,8 +156,8 @@ class MBMS
 
        redef fun collect(mmodules) do
                for mmodule in mmodules do
-                       var totc = mmodule.collect_intro_mclassdefs(mmodule.protected_view).length
-                       totc += mmodule.collect_redef_mclassdefs(mmodule.protected_view).length
+                       var totc = mmodule.collect_intro_mclassdefs(model_view).length
+                       totc += mmodule.collect_redef_mclassdefs(model_view).length
                        var ditc = mmodule.in_importation.depth
                        values[mmodule] = totc.to_f / (ditc + 1).to_f
                end
@@ -176,12 +172,8 @@ class CNVI
        redef fun name do return "cnvi"
        redef fun desc do return "class novelty index, contribution of the class to its branch in term of introductions"
 
-       # Mainmodule used to compute class hierarchy.
-       var mainmodule: MModule
-       private var protected_view: ModelView = mainmodule.model.protected_view is lateinit
-
        redef fun collect(mclasses) do
-               var cbms = new CBMS(mainmodule)
+               var cbms = new CBMS(mainmodule, model_view)
                for mclass in mclasses do
                        # compute branch mean size
                        var parents = mclass.in_hierarchy(mainmodule).direct_greaters
@@ -189,7 +181,7 @@ class CNVI
                                cbms.clear
                                cbms.collect(new HashSet[MClass].from(parents))
                                # compute class novelty index
-                               var locc = mclass.collect_accessible_mproperties(protected_view).length
+                               var locc = mclass.collect_accessible_mproperties(model_view).length
                                values[mclass] = locc.to_f / cbms.avg
                        else
                                values[mclass] = 0.0
@@ -207,7 +199,7 @@ class MNVI
        redef fun desc do return "module novelty index, contribution of the module to its branch in term of introductions"
 
        redef fun collect(mmodules) do
-               var mbms = new MBMS
+               var mbms = new MBMS(mainmodule, model_view)
                for mmodule in mmodules do
                        # compute branch mean size
                        var parents = mmodule.in_importation.direct_greaters
@@ -215,8 +207,8 @@ class MNVI
                                mbms.clear
                                mbms.collect(new HashSet[MModule].from(parents))
                                # compute module novelty index
-                               var locc = mmodule.collect_intro_mclassdefs(mmodule.protected_view).length
-                               locc += mmodule.collect_redef_mclassdefs(mmodule.protected_view).length
+                               var locc = mmodule.collect_intro_mclassdefs(model_view).length
+                               locc += mmodule.collect_redef_mclassdefs(model_view).length
                                values[mmodule] = locc.to_f / mbms.avg
                        else
                                values[mmodule] = 0.0
@@ -233,15 +225,11 @@ class CNVS
        redef fun name do return "cnvs"
        redef fun desc do return "class novelty score, importance of the contribution of the class to its branch"
 
-       # Mainmodule used to compute class hierarchy.
-       var mainmodule: MModule
-       private var protected_view: ModelView = mainmodule.model.protected_view is lateinit
-
        redef fun collect(mclasses) do
-               var cnvi = new CNVI(mainmodule)
+               var cnvi = new CNVI(mainmodule, model_view)
                cnvi.collect(mclasses)
                for mclass in mclasses do
-                       var locc = mclass.collect_local_mproperties(protected_view).length
+                       var locc = mclass.collect_local_mproperties(model_view).length
                        values[mclass] = cnvi.values[mclass] * locc.to_f
                end
        end
@@ -256,11 +244,11 @@ class MNVS
        redef fun desc do return "module novelty score, importance of the contribution of the module to its branch"
 
        redef fun collect(mmodules) do
-               var mnvi = new MNVI
+               var mnvi = new MNVI(mainmodule, model_view)
                mnvi.collect(mmodules)
                for mmodule in mmodules do
-                       var locc = mmodule.collect_intro_mclassdefs(mmodule.protected_view).length
-                       locc += mmodule.collect_redef_mclassdefs(mmodule.protected_view).length
+                       var locc = mmodule.collect_intro_mclassdefs(model_view).length
+                       locc += mmodule.collect_redef_mclassdefs(model_view).length
                        values[mmodule] = mnvi.values[mmodule] * locc.to_f
                end
        end
index dc944d8..1e9d190 100644 (file)
 module mmodules_metrics
 
 import metrics_base
+import model::model_collect
 
 redef class ToolContext
+
+       # MModules related metrics phase
        var mmodules_metrics_phase: Phase = new MModulesMetricsPhase(self, null)
 end
 
@@ -33,13 +36,23 @@ private class MModulesMetricsPhase
                var out = "{toolcontext.opt_dir.value or else "metrics"}/mmodules"
                out.mkdir
 
+               var model = toolcontext.modelbuilder.model
+               var model_view = model.private_view
+
                print toolcontext.format_h1("\n# MModules metrics")
 
                var metrics = new MetricSet
-               metrics.register(new MNOA, new MNOP, new MNOC, new MNOD, new MDIT)
-               metrics.register(new MNBI, new MNBR, new MNBCC, new MNBAC, new MNBIC)
+               metrics.register(new MNOA(mainmodule, model_view))
+               metrics.register(new MNOP(mainmodule, model_view))
+               metrics.register(new MNOC(mainmodule, model_view))
+               metrics.register(new MNOD(mainmodule, model_view))
+               metrics.register(new MDIT(mainmodule, model_view))
+               metrics.register(new MNBI(mainmodule, model_view))
+               metrics.register(new MNBR(mainmodule, model_view))
+               metrics.register(new MNBCC(mainmodule, model_view))
+               metrics.register(new MNBAC(mainmodule, model_view))
+               metrics.register(new MNBIC(mainmodule, model_view))
 
-               var model = toolcontext.modelbuilder.model
                var mmodules = new HashSet[MModule]
                for mpackage in model.mpackages do
 
@@ -68,9 +81,15 @@ private class MModulesMetricsPhase
 end
 
 # A metric about MModule
-interface MModuleMetric
+abstract class MModuleMetric
        super Metric
        redef type ELM: MModule
+
+       # Main module used for linearization
+       var mainmodule: MModule
+
+       # Model view used to collect and filter entities
+       var model_view: ModelView
 end
 
 # Module Metric: Number of Ancestors
@@ -143,6 +162,25 @@ class MDIT
        end
 end
 
+# Module Metric: Number of Accessible Definitions (of all kind)
+#
+# count all mclasses accessible by the module
+class MNBD
+       super MModuleMetric
+       super IntMetric
+       redef fun name do return "mnbd"
+       redef fun desc do return "number of definition accessibles in module"
+
+       redef fun collect(mmodules) do
+               for mmodule in mmodules do
+                       values[mmodule] = 0
+                       for a in mmodule.collect_ancestors(model_view) do
+                               values[mmodule] += a.intro_mclasses.length
+                       end
+               end
+       end
+end
+
 # Module Metric: Number of Introduction (of all kind)
 #
 # count all mclasses introduced by the module
@@ -250,5 +288,3 @@ class MNBEC
                end
        end
 end
-
-
index abd8180..f88c3ba 100644 (file)
@@ -22,6 +22,8 @@ import mclasses_metrics
 import semantize
 
 redef class ToolContext
+
+       # Nullable types related metrics
        var nullables_metrics_phase: Phase = new NullablesMetricsPhase(self, null)
 end
 
@@ -84,14 +86,6 @@ class CNBNA
        redef fun name do return "cnbna"
        redef fun desc do return "number of accessible nullable attributes (inherited + local)"
 
-       var mainmodule: MModule
-       var model_view: ModelView
-
-       init(mainmodule: MModule, model_view: ModelView) do
-               self.mainmodule = mainmodule
-               self.model_view = model_view
-       end
-
        redef fun collect(mclasses) do
                for mclass in mclasses do
                        var all = mclass.collect_accessible_mattributes(model_view)
@@ -114,7 +108,6 @@ end
 
 private class NullableSends
        super Visitor
-       var modelbuilder: ModelBuilder
        var nclassdef: AClassdef
 
        var total_sends: Int = 0
@@ -122,13 +115,6 @@ private class NullableSends
        var nullable_eq_sends: Int = 0
        var buggy_sends: Int = 0
 
-       # Get a new visitor on a classef to add type count in `typecount`.
-       init(modelbuilder: ModelBuilder, nclassdef: AClassdef)
-       do
-               self.modelbuilder = modelbuilder
-               self.nclassdef = nclassdef
-       end
-
        redef fun visit(n)
        do
                n.visit_all(self)
@@ -168,7 +154,7 @@ do
        # Visit all the source code to collect data
        for nmodule in modelbuilder.nmodules do
                for nclassdef in nmodule.n_classdefs do
-                       var visitor = new NullableSends(modelbuilder, nclassdef)
+                       var visitor = new NullableSends(nclassdef)
                        visitor.enter_visit(nclassdef)
                        total_sends += visitor.total_sends
                        nullable_sends += visitor.nullable_sends
index 46d319a..a4c3e28 100644 (file)
@@ -23,6 +23,8 @@ import mmodules_metrics
 import mclasses_metrics
 
 redef class ToolContext
+
+       # RTA related metrics phase
        var rta_metrics_phase: Phase = new RTAMetricsPhase(self, null)
 end
 
@@ -35,17 +37,20 @@ private class RTAMetricsPhase
                var out = "{toolcontext.opt_dir.value or else "metrics"}/rta"
                out.mkdir
 
+               var model = toolcontext.modelbuilder.model
+               var model_view = model.protected_view
+
                print toolcontext.format_h1("\n# RTA metrics")
 
                print toolcontext.format_h2("\n ## Live instances by mainmodules")
                var mmetrics = new MetricSet
-               mmetrics.register(new MNLC(toolcontext.modelbuilder))
-               mmetrics.register(new MNLT(toolcontext.modelbuilder))
-               mmetrics.register(new MNCT(toolcontext.modelbuilder))
-               mmetrics.register(new MNLI(toolcontext.modelbuilder))
-               mmetrics.register(new MNLM(toolcontext.modelbuilder))
-               mmetrics.register(new MNLMD(toolcontext.modelbuilder))
-               mmetrics.register(new MNLDD(toolcontext.modelbuilder))
+               mmetrics.register(new MNLC(mainmodule, model_view, toolcontext.modelbuilder))
+               mmetrics.register(new MNLT(mainmodule, model_view, toolcontext.modelbuilder))
+               mmetrics.register(new MNCT(mainmodule, model_view, toolcontext.modelbuilder))
+               mmetrics.register(new MNLI(mainmodule, model_view, toolcontext.modelbuilder))
+               mmetrics.register(new MNLM(mainmodule, model_view, toolcontext.modelbuilder))
+               mmetrics.register(new MNLMD(mainmodule, model_view, toolcontext.modelbuilder))
+               mmetrics.register(new MNLDD(mainmodule, model_view, toolcontext.modelbuilder))
                mmetrics.collect(new HashSet[MModule].from([mainmodule]))
                mmetrics.to_console(1, not toolcontext.opt_nocolors.value)
                if csv then mmetrics.to_csv.write_to_file("{out}/{mainmodule}.csv")
@@ -116,15 +121,21 @@ end
 
 # Summary metrics
 
+# RTA related metric that needs a `modelbuilder`
+class RTAMetric
+       super MModuleMetric
+
+       # Modelbuilder used to access AST
+       var modelbuilder: ModelBuilder
+end
+
 # MModule Metric: Number of Live Types
 class MNLI
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnli"
        redef fun desc do return "number of live instances in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
 
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
@@ -137,14 +148,11 @@ end
 
 # MModule Metric: Number of Live Types
 class MNLT
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnlt"
        redef fun desc do return "number of live mtypes in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
-
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
                        var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
@@ -156,14 +164,11 @@ end
 
 # MModule Metric: Number of Live Cast Types
 class MNCT
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnct"
        redef fun desc do return "number of live cast mtypes in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
-
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
                        var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
@@ -175,14 +180,11 @@ end
 
 # MModule Metric: Number of Live Classes
 class MNLC
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnlc"
        redef fun desc do return "number of live mclasses in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
-
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
                        var live = new HashSet[MClass]
@@ -198,14 +200,11 @@ end
 
 # MModule Metric: Number of Live Methods
 class MNLM
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnlm"
        redef fun desc do return "number of live methods in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
-
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
                        var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
@@ -217,14 +216,11 @@ end
 
 # MModule Metric: Number of Live MethodDefs
 class MNLMD
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnlmd"
        redef fun desc do return "number of live method definitions in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
-
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
                        var analysis = new RapidTypeAnalysis(modelbuilder, mainmodule)
@@ -236,14 +232,11 @@ end
 
 # MModule Metric: Number of Dead MethodDefs
 class MNLDD
-       super MModuleMetric
+       super RTAMetric
        super IntMetric
        redef fun name do return "mnldd"
        redef fun desc do return "number of dead method definitions in a mmodule"
 
-       var modelbuilder: ModelBuilder
-       init(modelbuilder: ModelBuilder) do self.modelbuilder = modelbuilder
-
        redef fun collect(mainmodules) do
                for mainmodule in mainmodules do
                        var dead = 0
@@ -359,9 +352,17 @@ end
 # rta redef
 
 redef class RapidTypeAnalysis
-       var cnli = new CNLI
-       var cnlc = new CNLC
+
+       # Class Live Instances
+       var cnli: CNLI is lazy do return new CNLI(mainmodule, modelbuilder.model.protected_view)
+
+       # Class Live Casts
+       var cnlc: CNLC is lazy do return new CNLC(mainmodule, modelbuilder.model.protected_view)
+
+       # Type Live Instances
        var tnli = new TNLI
+
+       # Rtpe Live Casts
        var tnlc = new TNLC
 
        redef fun add_new(recv, mtype) do
@@ -396,4 +397,3 @@ redef class MType
                return depth + 1
        end
 end
-
index ab1296c..d4e40f4 100644 (file)
@@ -424,6 +424,17 @@ redef class MClass
                return res
        end
 
+       # Collect all mmethods accessible by 'self' with `visibility >= min_visibility`.
+       #
+       # This include introduced, redefined, inherited mmethods.
+       fun collect_accessible_mmethods(view: ModelView): Set[MMethod] do
+               var set = new HashSet[MMethod]
+               set.add_all(collect_intro_mmethods(view))
+               set.add_all(collect_redef_mmethods(view))
+               set.add_all(collect_inherited_mmethods(view))
+               return set
+       end
+
        # Collect mattributes introduced in 'self' with `visibility >= min_visibility`.
        fun collect_intro_mattributes(view: ModelView): Set[MAttribute] do
                var res = new HashSet[MAttribute]
@@ -515,6 +526,17 @@ redef class MClass
                set.add_all(collect_inherited_inits(view))
                return set
        end
+
+       # Collect all virtual types accessible by 'self'  if accepted by `view`.
+       #
+       # This include introduced, redefined, inherited virtual types.
+       fun collect_accessible_vts(view: ModelView): Set[MVirtualTypeProp] do
+               var set = new HashSet[MVirtualTypeProp]
+               for mproperty in collect_accessible_mproperties(view) do
+                       if mproperty isa MVirtualTypeProp then set.add mproperty
+               end
+               return set
+       end
 end
 
 redef class MClassDef
index 73dab5b..e36a3ae 100644 (file)
@@ -104,11 +104,6 @@ redef class ModelBuilder
                end
 
                if mclass == null then
-                       if nclassdef isa AStdClassdef and nclassdef.n_kwredef != null then
-                               error(nclassdef, "Redef Error: no imported class `{name}` to refine.")
-                               return
-                       end
-
                        # Check for conflicting class full-names in the package
                        if mmodule.mgroup != null and mvisibility >= protected_visibility then
                                var mclasses = model.get_mclasses_by_name(name)
index af58044..7c6cdc1 100644 (file)
@@ -113,6 +113,7 @@ class APIRouter
                use("/inheritance/:id", new APIEntityInheritance(model, mainmodule))
                use("/graph/", new APIGraphRouter(model, mainmodule))
                use("/docdown/", new APIDocdown(model, mainmodule, modelbuilder))
+               use("/metrics/", new APIMetricsRouter(model, mainmodule))
        end
 end
 
index fb4b9bf..adb9543 100644 (file)
@@ -104,6 +104,12 @@ class AndroidToolchain
                dir = compile_dir
                if not dir.file_exists then dir.mkdir
 
+               # Insert an importation of the generated R class to all Java files from the FFI
+               for mod in compiler.mainmodule.in_importation.greaters do
+                       var java_ffi_file = mod.java_file
+                       if java_ffi_file != null then java_ffi_file.add "import {app_package}.R;"
+               end
+
                # compile normal C files
                super
 
@@ -112,7 +118,6 @@ class AndroidToolchain
                        if f isa ExternCFile then cfiles.add(f.filename.basename)
                end
 
-               # Is there an icon?
                var project_root = "."
                var mpackage = compiler.mainmodule.first_real_mmodule.mpackage
                if mpackage != null then
@@ -125,6 +130,35 @@ class AndroidToolchain
                        end
                end
 
+               # Set the default pretty application name
+"""<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">{{{app_name}}}</string>
+</resources>""".write_to_file "{android_project_root}/res/values/strings.xml"
+
+               # Copy assets, resources and libs where expected by the SDK
+
+               ## Collect path to all possible folder where we can find the `android` folder
+               var app_files = [project_root]
+               app_files.add_all project.files
+
+               for path in app_files do
+                       # Copy the assets folder
+                       var assets_dir = path / "assets"
+                       if assets_dir.file_exists then
+                               assets_dir = assets_dir.realpath
+                               toolcontext.exec_and_check(["cp", "-r", assets_dir, android_project_root], "Android project error")
+                       end
+
+                       # Copy the whole `android` folder
+                       var android_dir = path / "android"
+                       if android_dir.file_exists then
+                               android_dir = android_dir.realpath
+                               toolcontext.exec_and_check(["cp", "-r", android_dir, root_compile_dir], "Android project error")
+                       end
+               end
+
+               # Is there an icon?
                var resolutions = ["ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"]
                var icon_available = false
                for res in resolutions do
@@ -240,7 +274,7 @@ $(call import-module,android/native_app_glue)
 
                ### Link to png sources
                # libpng is not available on Android NDK
-               # FIXME make obtionnal when we have alternatives to mnit
+               # FIXME make optional when we have alternatives to mnit
                var nit_dir = toolcontext.nit_dir
                var share_dir =  nit_dir/"share/"
                if not share_dir.file_exists then
@@ -264,40 +298,6 @@ $(call import-module,android/native_app_glue)
 
                toolcontext.exec_and_check(["ln", "-s", "{share_dir}/libgc/arm/include/gc/",
                        "{compile_dir}/gc"], "Android project error")
-
-               # Copy assets, resources and libs where expected by the SDK
-
-               # Link to assets (for mnit and others)
-               var assets_dir = project_root / "assets"
-               if assets_dir.file_exists then
-                       assets_dir = assets_dir.realpath
-                       var target_assets_dir = "{android_project_root}/assets"
-                       if not target_assets_dir.file_exists then
-                               toolcontext.exec_and_check(["ln", "-s", assets_dir, target_assets_dir], "Android project error")
-                       end
-               end
-
-               # Copy the res folder
-               var res_dir = project_root / "android/res"
-               if res_dir.file_exists then
-                       # copy the res folder to the compile dir
-                       res_dir = res_dir.realpath
-                       toolcontext.exec_and_check(["cp", "-R", res_dir, android_project_root], "Android project error")
-               end
-
-               if not res_dir.file_exists or not "{res_dir}/values/strings.xml".file_exists then
-                       # Create our own custom `res/values/string.xml` with the App name
-"""<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="app_name">{{{app_name}}}</string>
-</resources>""".write_to_file "{android_project_root}/res/values/strings.xml"
-               end
-
-               # Copy the libs folder
-               var libs_dir = project_root / "android/libs"
-               if libs_dir.file_exists then
-                       toolcontext.exec_and_check(["cp", "-r", libs_dir, android_project_root], "Android project error")
-               end
        end
 
        redef fun write_makefile(compile_dir, cfiles)
index 1d60f15..9cfdec9 100644 (file)
@@ -44,6 +44,9 @@ class AppProject
                return local_time_s.to_i
        end
 
+       # Extra folders where to find platform specific resource files
+       var files = new Array[String]
+
        private var modelbuilder: ModelBuilder
        private var mainmodule: MModule
 
@@ -64,6 +67,9 @@ class AppProject
                        if val != null then namespace = val
                end
 
+               var annots = modelbuilder.collect_annotations_on_modules("app_files", mainmodule)
+               for a in annots do files.add_all a.as_relative_paths(modelbuilder)
+
                modelbuilder.toolcontext.check_errors
        end
 
@@ -134,4 +140,29 @@ redef class AAnnotation
 
                return version_fields.join(".")
        end
+
+       # Parse all arguments as paths relative to the declaring module
+       #
+       # If no arguments are given, then use the parent directory of the module.
+       private fun as_relative_paths(modelbuilder: ModelBuilder): Array[String]
+       do
+               var paths = new Array[String]
+
+               var file = location.file
+               if file == null then return paths
+
+               var args = n_args
+               if args.is_empty then
+                       paths.add file.filename.dirname
+               else
+                       for arg in args do
+                               var val = arg.as_string
+                               if val != null then
+                                       paths.add file.filename.dirname/val
+                               else modelbuilder.error(arg, "Syntax Error: `app_files` expects String literals as arguments.")
+                       end
+               end
+
+               return paths
+       end
 end
index c221598..76fefea 100644 (file)
@@ -95,15 +95,22 @@ private class IOSToolchain
                        end
                end
 
-               var icon_dir = project_root / "ios" / "AppIcon.appiconset"
-               var icons_found = icon_dir.file_exists
-               if icons_found then
+               # Copy all resources
+               var app_files = [project_root]
+               app_files.add_all app_project.files
+
+               var icons_found = false
 
-                       # Prepare the `Assets.xcassets` folder
-                       var target_assets_dir = compile_dir / "Assets.xcassets"
-                       if not target_assets_dir.file_exists then target_assets_dir.mkdir
+               for path in app_files do
+                       var icon_dir = path / "ios" / "AppIcon.appiconset"
+                       if icon_dir.file_exists then
+                               icons_found = true
 
-                       """
+                               # Prepare the `Assets.xcassets` folder
+                               var target_assets_dir = compile_dir / "Assets.xcassets"
+                               if not target_assets_dir.file_exists then target_assets_dir.mkdir
+
+                               """
 {
   "info" : {
        "version" : 1,
@@ -111,9 +118,10 @@ private class IOSToolchain
   }
 }""".write_to_file target_assets_dir / "Contents.json"
 
-                       # copy the res folder to the compile dir
-                       icon_dir = icon_dir.realpath
-                       toolcontext.exec_and_check(["cp", "-R", icon_dir, target_assets_dir], "iOS project error")
+                               # copy the res folder to the compile dir
+                               icon_dir = icon_dir.realpath
+                               toolcontext.exec_and_check(["cp", "-R", icon_dir, target_assets_dir], "iOS project error")
+                       end
                end
 
                # TODO Register asset files
diff --git a/src/web/api_metrics.nit b/src/web/api_metrics.nit
new file mode 100644 (file)
index 0000000..30a53f1
--- /dev/null
@@ -0,0 +1,245 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+module api_metrics
+
+import web_base
+import metrics
+
+# Group all api handlers in one router.
+class APIMetricsRouter
+       super Router
+
+       # Model to pass to handlers.
+       var model: Model
+
+       # Mainmodule to pass to handlers.
+       var mainmodule: MModule
+
+       init do
+               use("/structural/:id", new APIStructuralMetrics(model, mainmodule))
+       end
+end
+
+class APIStructuralMetrics
+       super APIHandler
+
+       private fun mclasses_metrics: MetricSet do
+               var metrics = new MetricSet
+               metrics.register(new CNOA(mainmodule, view))
+               metrics.register(new CNOP(mainmodule, view))
+               metrics.register(new CNOC(mainmodule, view))
+               metrics.register(new CNOD(mainmodule, view))
+               metrics.register(new CNOAC(mainmodule, view))
+               metrics.register(new CNOAA(mainmodule, view))
+               metrics.register(new CNOAI(mainmodule, view))
+               metrics.register(new CDIT(mainmodule, view))
+               metrics.register(new CNBP(mainmodule, view))
+               metrics.register(new CNBA(mainmodule, view))
+               metrics.register(new CNBM(mainmodule, view))
+               metrics.register(new CNBI(mainmodule, view))
+               metrics.register(new CNBV(mainmodule, view))
+               metrics.register(new CNBIP(mainmodule, view))
+               metrics.register(new CNBRP(mainmodule, view))
+               metrics.register(new CNBHP(mainmodule, view))
+               metrics.register(new CNBLP(mainmodule, view))
+               return metrics
+       end
+
+       private fun mmodules_metrics: MetricSet do
+               var metrics = new MetricSet
+               metrics.register(new MNOA(mainmodule, view))
+               metrics.register(new MNOP(mainmodule, view))
+               metrics.register(new MNOC(mainmodule, view))
+               metrics.register(new MNOD(mainmodule, view))
+               metrics.register(new MDIT(mainmodule, view))
+               metrics.register(new MNBD(mainmodule, view))
+               metrics.register(new MNBI(mainmodule, view))
+               metrics.register(new MNBR(mainmodule, view))
+               metrics.register(new MNBCC(mainmodule, view))
+               metrics.register(new MNBAC(mainmodule, view))
+               metrics.register(new MNBIC(mainmodule, view))
+               return metrics
+       end
+
+       redef fun get(req, res) do
+               var mentity = mentity_from_uri(req, res)
+               if mentity == null then
+                       res.error 404
+                       return
+               end
+               var metrics = mentity.collect_metrics(self)
+               if metrics == null then
+                       res.error 404
+                       return
+               end
+               res.json metrics
+       end
+end
+
+redef class MEntity
+       private fun collect_metrics(h: APIStructuralMetrics): nullable JsonObject do return null
+end
+
+redef class MPackage
+       redef fun collect_metrics(h) do
+               var mclasses = new HashSet[MClass]
+               for mgroup in self.mgroups do
+                       for mmodule in mgroup.mmodules do mclasses.add_all mmodule.intro_mclasses
+               end
+
+               var mclasses_metrics = h.mclasses_metrics
+               mclasses_metrics.collect(new HashSet[MClass].from(mclasses))
+
+               var mmodules = new HashSet[MModule]
+               for mgroup in self.mgroups do
+                       mmodules.add_all mgroup.mmodules
+               end
+
+               var mmodules_metrics = h.mmodules_metrics
+               mmodules_metrics.collect(new HashSet[MModule].from(mmodules))
+
+               var metrics = new JsonObject
+               metrics["mclasses"] = mclasses_metrics
+               metrics["mmodules"] = mmodules_metrics
+               return metrics
+       end
+end
+
+redef class MGroup
+       redef fun collect_metrics(h) do
+               var mclasses = new HashSet[MClass]
+               for mmodule in self.mmodules do mclasses.add_all mmodule.intro_mclasses
+
+               var mclasses_metrics = h.mclasses_metrics
+               mclasses_metrics.collect(new HashSet[MClass].from(mclasses))
+
+               var mmodules_metrics = h.mmodules_metrics
+               mmodules_metrics.collect(new HashSet[MModule].from(mmodules))
+
+               var metrics = new JsonObject
+               metrics["mclasses"] = mclasses_metrics
+               metrics["mmodules"] = mmodules_metrics
+               return metrics
+       end
+end
+
+redef class MModule
+       redef fun collect_metrics(h) do
+               var mclasses_metrics = h.mclasses_metrics
+               mclasses_metrics.collect(new HashSet[MClass].from(intro_mclasses))
+
+               var mmodule_metrics = h.mmodules_metrics
+               mmodule_metrics.collect(new HashSet[MModule].from([self]))
+
+               var metrics = new JsonObject
+               metrics["mclasses"] = mclasses_metrics
+               metrics["mmodule"] = mmodule_metrics
+               return metrics
+       end
+end
+
+redef class MClass
+       redef fun collect_metrics(h) do
+               var mclass_metrics = h.mclasses_metrics
+               mclass_metrics.collect(new HashSet[MClass].from([self]))
+
+               var metrics = new JsonObject
+               metrics["mclass"] = mclass_metrics
+               return metrics
+       end
+end
+
+redef class MetricSet
+       super Jsonable
+
+       fun json: JsonObject do
+               var obj = new JsonObject
+               for metric in metrics do
+                       obj[metric.name] = metric
+               end
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
+end
+
+redef class Metric
+       super Jsonable
+
+       fun json: JsonObject do
+               var obj = new JsonObject
+               obj["name"] = name
+               obj["desc"] = desc
+               obj["empty"] = values.is_empty
+               if values.not_empty then obj["avg"] = avg
+               if values.not_empty then obj["std_dev"] = std_dev
+               if values.not_empty then obj["threshold"] = threshold
+               return obj
+       end
+
+       redef fun to_json do return json.to_json
+end
+
+redef class IntMetric
+       redef fun json do
+               var obj = super
+               if values.not_empty then obj["sum"] = sum
+               return obj
+       end
+end
+
+redef class FloatMetric
+       redef fun json do
+               var obj = super
+               if values.not_empty then obj["sum"] = sum
+               return obj
+       end
+end
+
+redef class MModuleMetric
+       redef fun json do
+               var obj = super
+               if values.not_empty then obj["min"] = min
+               if values.not_empty then obj["max"] = max
+               var values = new JsonObject
+               for value in sort do
+                       var v = self[value]
+                       var vobj = new JsonObject
+                       vobj["mentity"] = value
+                       vobj["value"] = if v isa Jsonable then v else v.to_s
+                       values[value.full_name] = vobj
+               end
+               obj["values"] = values
+               return obj
+       end
+end
+
+redef class MClassMetric
+       redef fun json do
+               var obj = super
+               if values.not_empty then obj["min"] = min
+               if values.not_empty then obj["max"] = max
+               var values = new JsonObject
+               for value in sort do
+                       var v = self[value]
+                       var vobj = new JsonObject
+                       vobj["mentity"] = value
+                       vobj["value"] = if v isa Jsonable then v else v.to_s
+                       values[value.full_name] = vobj
+               end
+               obj["values"] = values
+               return obj
+       end
+end
index c6bb476..63571c0 100644 (file)
@@ -19,4 +19,5 @@ import api_model
 import api_catalog
 import api_graph
 import api_docdown
+import api_metrics
 import api_feedback
index 6d734f6..2eb19e5 100644 (file)
   bivariants: 0 (na%)
   invariants: 0 (na%)
   total: 0
-
-# MModules metrics
-
- ## package base_simple3
-  `- group base_simple3>
-       mnoa: number of ancestor modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnop: number of parent modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnoc: number of child modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnod: number of descendant modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mdit: depth in module tree
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnbi: number of introduction in module
-         avg: 7.0
-         max: base_simple3 (7)
-         min: base_simple3 (7)
-         std: 0.0
-         sum: 7
-       mnbr: number of refinement in module
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnbcc: number of concrete class in module (intro + redef)
-         avg: 4.0
-         max: base_simple3 (4)
-         min: base_simple3 (4)
-         std: 0.0
-         sum: 4
-       mnbac: number of abstract class in module (intro + redef)
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnbic: number of interface in module (intro + redef)
-         avg: 1.0
-         max: base_simple3 (1)
-         min: base_simple3 (1)
-         std: 0.0
-         sum: 1
-
- ## global metrics
-       mnoa: number of ancestor modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnop: number of parent modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnoc: number of child modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnod: number of descendant modules
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mdit: depth in module tree
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnbi: number of introduction in module
-         avg: 7.0
-         max: base_simple3 (7)
-         min: base_simple3 (7)
-         std: 0.0
-         sum: 7
-       mnbr: number of refinement in module
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnbcc: number of concrete class in module (intro + redef)
-         avg: 4.0
-         max: base_simple3 (4)
-         min: base_simple3 (4)
-         std: 0.0
-         sum: 4
-       mnbac: number of abstract class in module (intro + redef)
-         avg: 0.0
-         max: base_simple3 (0)
-         min: base_simple3 (0)
-         std: 0.0
-         sum: 0
-       mnbic: number of interface in module (intro + redef)
-         avg: 1.0
-         max: base_simple3 (1)
-         min: base_simple3 (1)
-         std: 0.0
-         sum: 1
 --- Poset metrics ---
 ## Module importation hierarchy
 Number of nodes: 1
@@ -410,6 +283,24 @@ generating module_hierarchy.dot
          min: Object (0)
          std: 0.845
          sum: 3
+       cnbi: number of accessible constructors (inherited + local)
+         avg: 1.0
+         max: B (2)
+         min: Object (1)
+         std: 0.378
+         sum: 8
+       cnbm: number of accessible methods (inherited + local)
+         avg: 3.0
+         max: B (5)
+         min: Object (1)
+         std: 1.773
+         sum: 21
+       cnbv: number of accessible virtual types (inherited + local)
+         avg: 0.0
+         max: Object (0)
+         min: Object (0)
+         std: 0.0
+         sum: 0
        cnbip: number of introduced properties
          avg: 2.0
          max: C (6)
@@ -472,6 +363,24 @@ generating module_hierarchy.dot
          min: Object (0)
          std: 0.845
          sum: 3
+       cnbi: number of accessible constructors (inherited + local)
+         avg: 1.0
+         max: B (2)
+         min: Object (1)
+         std: 0.378
+         sum: 8
+       cnbm: number of accessible methods (inherited + local)
+         avg: 3.0
+         max: B (5)
+         min: Object (1)
+         std: 1.773
+         sum: 21
+       cnbv: number of accessible virtual types (inherited + local)
+         avg: 0.0
+         max: Object (0)
+         min: Object (0)
+         std: 0.0
+         sum: 0
        cnbip: number of introduced properties
          avg: 2.0
          max: C (6)
@@ -491,6 +400,133 @@ generating module_hierarchy.dot
          std: 0.926
          sum: 6
 
+# MModules metrics
+
+ ## package base_simple3
+  `- group base_simple3>
+       mnoa: number of ancestor modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnop: number of parent modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnoc: number of child modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnod: number of descendant modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mdit: depth in module tree
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnbi: number of introduction in module
+         avg: 7.0
+         max: base_simple3 (7)
+         min: base_simple3 (7)
+         std: 0.0
+         sum: 7
+       mnbr: number of refinement in module
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnbcc: number of concrete class in module (intro + redef)
+         avg: 4.0
+         max: base_simple3 (4)
+         min: base_simple3 (4)
+         std: 0.0
+         sum: 4
+       mnbac: number of abstract class in module (intro + redef)
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnbic: number of interface in module (intro + redef)
+         avg: 1.0
+         max: base_simple3 (1)
+         min: base_simple3 (1)
+         std: 0.0
+         sum: 1
+
+ ## global metrics
+       mnoa: number of ancestor modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnop: number of parent modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnoc: number of child modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnod: number of descendant modules
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mdit: depth in module tree
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnbi: number of introduction in module
+         avg: 7.0
+         max: base_simple3 (7)
+         min: base_simple3 (7)
+         std: 0.0
+         sum: 7
+       mnbr: number of refinement in module
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnbcc: number of concrete class in module (intro + redef)
+         avg: 4.0
+         max: base_simple3 (4)
+         min: base_simple3 (4)
+         std: 0.0
+         sum: 4
+       mnbac: number of abstract class in module (intro + redef)
+         avg: 0.0
+         max: base_simple3 (0)
+         min: base_simple3 (0)
+         std: 0.0
+         sum: 0
+       mnbic: number of interface in module (intro + redef)
+         avg: 1.0
+         max: base_simple3 (1)
+         min: base_simple3 (1)
+         std: 0.0
+         sum: 1
+
 # Inheritance metrics
 
  ## package base_simple3