├── demo ├── db │ └── pivot │ │ ├── dbex.lck │ │ ├── db.lck │ │ ├── log │ │ ├── log.ctrl │ │ ├── log1.dat │ │ └── logmirror.ctrl │ │ ├── seg0 │ │ ├── c10.dat │ │ ├── c101.dat │ │ ├── c111.dat │ │ ├── c121.dat │ │ ├── c130.dat │ │ ├── c141.dat │ │ ├── c150.dat │ │ ├── c161.dat │ │ ├── c171.dat │ │ ├── c180.dat │ │ ├── c191.dat │ │ ├── c1a1.dat │ │ ├── c1b1.dat │ │ ├── c1c0.dat │ │ ├── c1d1.dat │ │ ├── c1e0.dat │ │ ├── c1f1.dat │ │ ├── c20.dat │ │ ├── c200.dat │ │ ├── c211.dat │ │ ├── c221.dat │ │ ├── c230.dat │ │ ├── c241.dat │ │ ├── c251.dat │ │ ├── c260.dat │ │ ├── c271.dat │ │ ├── c281.dat │ │ ├── c290.dat │ │ ├── c2a1.dat │ │ ├── c2b1.dat │ │ ├── c2c1.dat │ │ ├── c2d0.dat │ │ ├── c2e1.dat │ │ ├── c2f0.dat │ │ ├── c300.dat │ │ ├── c31.dat │ │ ├── c311.dat │ │ ├── c321.dat │ │ ├── c331.dat │ │ ├── c340.dat │ │ ├── c351.dat │ │ ├── c361.dat │ │ ├── c371.dat │ │ ├── c380.dat │ │ ├── c391.dat │ │ ├── c3a1.dat │ │ ├── c3b1.dat │ │ ├── c3e0.dat │ │ ├── c41.dat │ │ ├── c51.dat │ │ ├── c60.dat │ │ ├── c71.dat │ │ ├── c81.dat │ │ ├── c90.dat │ │ ├── ca1.dat │ │ ├── cb1.dat │ │ ├── cc0.dat │ │ ├── cd1.dat │ │ ├── ce1.dat │ │ └── cf0.dat │ │ └── service.properties ├── src │ ├── main │ │ ├── webapp │ │ │ ├── index.html │ │ │ ├── images │ │ │ │ ├── glyphicons-halflings.png │ │ │ │ └── glyphicons-halflings-white.png │ │ │ ├── css │ │ │ │ └── style.css │ │ │ └── WEB-INF │ │ │ │ └── web.xml │ │ ├── resources │ │ │ └── log4j.properties │ │ └── java │ │ │ └── ro │ │ │ └── fortsoft │ │ │ └── wicket │ │ │ └── pivot │ │ │ └── demo │ │ │ ├── PivotPage.html │ │ │ ├── PivotApplication.java │ │ │ ├── PivotDataSourceHandler.java │ │ │ ├── PivotConfigSessionStorage.java │ │ │ └── PivotPage.java │ └── test │ │ └── java │ │ └── ro │ │ └── fortsoft │ │ └── wicket │ │ └── pivot │ │ └── Start.java ├── init_derby.sql └── pom.xml ├── wicket-pivot.png ├── .gitignore ├── wicket-pivot ├── src │ └── main │ │ └── java │ │ └── ro │ │ └── fortsoft │ │ └── wicket │ │ └── pivot │ │ ├── wicket-package_de.properties │ │ ├── web │ │ ├── res │ │ │ ├── sort-behavior.template.js │ │ │ ├── pivot.js │ │ │ ├── pivot.css │ │ │ ├── jquery.json-2.2.min.js │ │ │ ├── jquery.dropdown.css │ │ │ └── jquery.dropdown.js │ │ ├── AggregatorPanel.html │ │ ├── PivotFieldActionsPanel.html │ │ ├── PivotAreaPanel.html │ │ ├── FieldCalculationPanel.html │ │ ├── PivotTable.html │ │ ├── PivotConfigStoragePanel.html │ │ ├── AreaChangedEvent.java │ │ ├── PivotPanel.html │ │ ├── PivotResourcesBehavior.java │ │ ├── SortableAjaxBehavior.java │ │ ├── AggregatorPanel.java │ │ ├── PivotConfigStoragePanel.java │ │ ├── PivotFieldActionsPanel.java │ │ ├── FieldCalculationPanel.java │ │ ├── PivotSettings.java │ │ ├── PivotAreaPanel.java │ │ ├── PivotTable.java │ │ └── PivotPanel.java │ │ ├── wicket-package_ro.properties │ │ ├── wicket-package.properties │ │ ├── PivotFieldActionsFactory.java │ │ ├── tree │ │ ├── Tree.java │ │ ├── TreeIterator.java │ │ ├── TreeHelper.java │ │ └── Node.java │ │ ├── PivotUtils.java │ │ ├── PivotDataSource.java │ │ ├── exporter │ │ ├── PivotExporter.java │ │ └── PivotCsvExporter.java │ │ ├── config │ │ ├── IPivotConfigStorage.java │ │ └── PivotConfig.java │ │ ├── DefaultPivotFieldActionsFactory.java │ │ ├── PivotModel.java │ │ ├── ResultSetPivotDataSource.java │ │ ├── PivotField.java │ │ ├── FieldCalculation.java │ │ ├── Aggregator.java │ │ ├── PivotFieldAction.java │ │ └── DefaultPivotModel.java └── pom.xml ├── wicket-pivot-exporter ├── pom.xml └── src │ └── main │ └── java │ └── ro │ └── fortsoft │ └── wicket │ └── pivot │ └── exporter │ ├── PivotExporters.java │ └── PivotXlsExporter.java ├── pom.xml └── README.md /demo/db/pivot/dbex.lck: -------------------------------------------------------------------------------- 1 |  -------------------------------------------------------------------------------- /demo/db/pivot/db.lck: -------------------------------------------------------------------------------- 1 | $a816c00e-0138-fbc2-5b5e-000003fd9cb0 -------------------------------------------------------------------------------- /wicket-pivot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/wicket-pivot.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .project 3 | .classpath 4 | .settings 5 | demo/data/ 6 | .idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /demo/db/pivot/log/log.ctrl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/log/log.ctrl -------------------------------------------------------------------------------- /demo/db/pivot/log/log1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/log/log1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c10.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c10.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c101.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c101.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c111.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c111.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c121.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c121.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c130.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c130.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c141.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c141.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c150.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c150.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c161.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c161.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c171.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c171.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c180.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c180.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c191.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c191.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c1a1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c1a1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c1b1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c1b1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c1c0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c1c0.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c1d1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c1d1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c1e0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c1e0.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c1f1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c1f1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c20.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c20.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c200.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c200.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c211.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c211.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c221.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c221.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c230.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c230.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c241.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c241.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c251.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c251.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c260.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c260.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c271.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c271.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c281.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c281.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c290.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c290.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c2a1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c2a1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c2b1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c2b1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c2c1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c2c1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c2d0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c2d0.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c2e1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c2e1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c2f0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c2f0.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c300.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c300.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c31.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c31.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c311.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c311.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c321.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c321.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c331.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c331.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c340.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c340.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c351.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c351.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c361.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c361.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c371.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c371.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c380.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c380.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c391.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c391.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c3a1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c3a1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c3b1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c3b1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c3e0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c3e0.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c41.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c41.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c51.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c51.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c60.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c60.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c71.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c71.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c81.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c81.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/c90.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/c90.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/ca1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/ca1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/cb1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/cb1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/cc0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/cc0.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/cd1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/cd1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/ce1.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/ce1.dat -------------------------------------------------------------------------------- /demo/db/pivot/seg0/cf0.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/seg0/cf0.dat -------------------------------------------------------------------------------- /demo/db/pivot/log/logmirror.ctrl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/db/pivot/log/logmirror.ctrl -------------------------------------------------------------------------------- /demo/src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /demo/src/main/webapp/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/src/main/webapp/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /demo/src/main/webapp/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/demo/src/main/webapp/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/wicket-package_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/decebals/wicket-pivot/HEAD/wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/wicket-package_de.properties -------------------------------------------------------------------------------- /demo/src/main/webapp/css/style.css: -------------------------------------------------------------------------------- 1 | @CHARSET "ISO-8859-1"; 2 | 3 | #wicketDebugLink { 4 | font-size: 10px; 5 | } 6 | 7 | div.wicket-modal div.w_caption { 8 | height: 1.7em !important; 9 | } 10 | 11 | h3.w_captionText { 12 | font-size: 13px !important; 13 | } 14 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/res/sort-behavior.template.js: -------------------------------------------------------------------------------- 1 | $('#${component}').sortable({ 2 | connectWith: '.fields', 3 | forcePlaceholderSize: true, 4 | placeholder: 'pivot-placeholder', 5 | cursor: 'move', 6 | opacity: 0.5, 7 | stop: function(event, ui) { 8 | ${stopBehavior} 9 | } 10 | }); -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/wicket-package_ro.properties: -------------------------------------------------------------------------------- 1 | # pivot areas 2 | row=RAND 3 | column=COLOANA 4 | data=DATE 5 | unused=NEUTILIZATE 6 | 7 | showGrandTotalForColumn=Afiseaza total general pentru coloana 8 | showGrandTotalForRow=Afiseaza total general pentru rand 9 | autoCalculate=Calcul automat 10 | showPivot=Afiseaza pivot 11 | 12 | downloadAs=Descarca ca ${} 13 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/AggregatorPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | Ok 8 |
9 |
10 | 11 | -------------------------------------------------------------------------------- /demo/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=INFO,Console 2 | 3 | log4j.appender.Console=org.apache.log4j.ConsoleAppender 4 | log4j.appender.Console.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.Console.layout.conversionPattern=%-5p - %-26.26c{1} - %m\n 6 | 7 | log4j.logger.org.apache.wicket=INFO 8 | log4j.logger.org.apache.wicket.protocol.http.HttpSessionStore=INFO 9 | log4j.logger.org.apache.wicket.version=INFO 10 | log4j.logger.org.apache.wicket.RequestCycle=INFO 11 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/wicket-package.properties: -------------------------------------------------------------------------------- 1 | # pivot areas 2 | row=ROW 3 | column=COLUMN 4 | data=DATA 5 | unused=UNUSED 6 | 7 | showGrandTotalForColumn=Show grand total for column 8 | showGrandTotalForRow=Show grand total for row 9 | autoCalculate=Auto calculate 10 | showPivot=Show pivot 11 | 12 | downloadAs=Download as ${} 13 | 14 | loadSaveConfiguration=Load/Save Configuration 15 | loadConfiguration=Load 16 | deleteConfiguration=Delete 17 | saveConfiguration=Save configuration 18 | savedConfigurations=Your saved configurations: -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotFieldActionsPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotAreaPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
    6 |
  1. 7 | 8 |
    9 |
  2. 10 |
11 |
12 |
13 | 14 | -------------------------------------------------------------------------------- /demo/init_derby.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE STATISTIC; 2 | 3 | CREATE TABLE STATISTIC ( 4 | REGION varchar(20), 5 | SALESMAN varchar(20), 6 | "YEAR" int, 7 | MONTH int, 8 | SALES int, 9 | COST int 10 | ); 11 | 12 | INSERT INTO STATISTIC (REGION, SALESMAN, "YEAR", MONTH, SALES, COST) VALUES 13 | ('1', 'John', 2005, 3, 1000, 100), 14 | ('1', 'David', 2005, 3, 1300, 200), 15 | ('1', 'Sophia', 2005, 1, 312, 400), 16 | ('2', 'David', 2005, 3, 1234, 0), 17 | ('2', 'Sophia', 2006, 3, 543, 0), 18 | ('3', 'John', 2006, 4, 534, 0), 19 | ('3', 'David', 2006, 6, 423, 0), 20 | ('3', 'John', 2007, 8, 134, 0), 21 | ('4', 'Sophia', 2007, 4, 423, 0), 22 | ('4', 'David', 2007, 2, 466, 0), 23 | ('4', 'David', 2008, 4, 563, 0), 24 | ('4', 'John', 2008, 5, 222, 0), 25 | ('1', 'Oliver', 2005, 2, 0, 0) 26 | -------------------------------------------------------------------------------- /demo/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | Pivot 9 | 10 | 11 | pivot 12 | org.apache.wicket.protocol.http.WicketFilter 13 | 14 | applicationClassName 15 | ro.fortsoft.wicket.pivot.demo.PivotApplication 16 | 17 | 18 | 19 | 20 | pivot 21 | /app/* 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/FieldCalculationPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 |
7 | 8 |
9 | 10 |
11 | 12 |
13 | Ok 14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /demo/src/main/java/ro/fortsoft/wicket/pivot/demo/PivotPage.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |

Simple wicket pivot table

16 |

A java web pivot table with SQL select as data source.

17 |
18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotTable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/res/pivot.js: -------------------------------------------------------------------------------- 1 | function serializeFieldLocations(/*areasId*/) { 2 | // console.log(areasId); 3 | var items = []; 4 | // $("#" + areasId).find("ol.fields").each(function() { 5 | $("ol.fields").each(function() { 6 | var areaId = $(this).attr("id"); 7 | var areaName = areaId.substring(areaId.indexOf("-") + 1, areaId.lastIndexOf("-")); // "area--" 8 | $(this).children().each(function(i) { 9 | var fieldId = $(this).attr("id"); 10 | var fieldIndex = fieldId.substring(6); // "field-" 11 | // create item object for current panel 12 | var item = { 13 | areaName : areaName, 14 | fieldIndex : fieldIndex, 15 | sortIndex : i 16 | }; 17 | 18 | // push item object into items array 19 | items.push(item); 20 | }); 21 | }); 22 | 23 | // pass items variable to server to save state 24 | var data = $.toJSON(items); 25 | 26 | return data; 27 | } 28 | -------------------------------------------------------------------------------- /demo/db/pivot/service.properties: -------------------------------------------------------------------------------- 1 | #/stuff/work/wicket-pivot/demo/db/pivot 2 | # ******************************************************************** 3 | # *** Please do NOT edit this file. *** 4 | # *** CHANGING THE CONTENT OF THIS FILE MAY CAUSE DATA CORRUPTION. *** 5 | # ******************************************************************** 6 | #Thu Aug 02 16:20:46 EEST 2012 7 | SysschemasIndex2Identifier=225 8 | SyscolumnsIdentifier=144 9 | SysconglomeratesIndex1Identifier=49 10 | SysconglomeratesIdentifier=32 11 | SyscolumnsIndex2Identifier=177 12 | SysschemasIndex1Identifier=209 13 | SysconglomeratesIndex3Identifier=81 14 | SystablesIndex2Identifier=129 15 | SyscolumnsIndex1Identifier=161 16 | derby.serviceProtocol=org.apache.derby.database.Database 17 | SysschemasIdentifier=192 18 | derby.storage.propertiesId=16 19 | SysconglomeratesIndex2Identifier=65 20 | derby.serviceLocale=en_US 21 | SystablesIdentifier=96 22 | SystablesIndex1Identifier=113 23 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotConfigStoragePanel.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 5 | 8 |
9 | 10 | 11 | Your saved configurations: 12 | 14 | 15 | 16 | 19 | 24 | 25 |
17 | Load 18 |
20 |
21 | Delete 22 |
23 |
26 |
-------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/PivotFieldActionsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * @author Decebal Suiu 19 | */ 20 | public interface PivotFieldActionsFactory { 21 | 22 | public List createPivotFieldActions(PivotField field, PivotModel model); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/tree/Tree.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.tree; 14 | 15 | import java.io.Serializable; 16 | 17 | /** 18 | * @author Decebal Suiu 19 | */ 20 | public class Tree implements Serializable { 21 | 22 | private static final long serialVersionUID = 1L; 23 | 24 | private Node root; 25 | 26 | public Tree(Node root) { 27 | this.root = root; 28 | } 29 | 30 | public Node getRoot() { 31 | return this.root; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /wicket-pivot-exporter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ro.fortsoft.wicket.pivot 6 | wicket-pivot-parent 7 | 0.6.0-SNAPHOT 8 | 9 | 10 | 4.0.0 11 | wicket-pivot-exporter 12 | 0.6.0-SNAPHOT 13 | jar 14 | Wicket Pivot Exporter 15 | Simple web pivot table using Apache Wicket Exporter Plugins 16 | 17 | 18 | 19 | 20 | ro.fortsoft.wicket.pivot 21 | wicket-pivot 22 | ${project.version} 23 | 24 | 25 | 26 | 27 | org.apache.poi 28 | poi 29 | 4.1.1 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/AreaChangedEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import org.apache.wicket.ajax.AjaxRequestTarget; 16 | 17 | /** 18 | * @author Decebal Suiu 19 | */ 20 | public class AreaChangedEvent { 21 | 22 | private final AjaxRequestTarget target; 23 | 24 | public AreaChangedEvent(AjaxRequestTarget target) { 25 | this.target = target; 26 | } 27 | 28 | public AjaxRequestTarget getAjaxRequestTarget() { 29 | return target; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /wicket-pivot-exporter/src/main/java/ro/fortsoft/wicket/pivot/exporter/PivotExporters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.exporter; 14 | 15 | /** 16 | * Factory for the pivot exporter 17 | */ 18 | public class PivotExporters { 19 | private PivotExporters() { 20 | } 21 | 22 | /** 23 | * Factory to get all existing Pivot Exporter 24 | * 25 | * @return a new List of exporter instances 26 | */ 27 | public static PivotExporter[] createAllExporter() { 28 | return new PivotExporter[] { new PivotXlsExporter(), new PivotCsvExporter() }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/PivotUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.util.List; 16 | 17 | import ro.fortsoft.wicket.pivot.FieldCalculation.FieldValueProvider; 18 | 19 | /** 20 | * @author Decebal Suiu 21 | */ 22 | public class PivotUtils { 23 | 24 | public static Number getSummary(PivotField dataField, List values, FieldValueProvider fieldValueProvider) { 25 | if(dataField.getFieldCalculation() != null ) 26 | return (Number)dataField.getFieldCalculation().init().calculate(fieldValueProvider); 27 | return (Number) dataField.getAggregator().init().addAll(values).getResult(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /wicket-pivot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ro.fortsoft.wicket.pivot 6 | wicket-pivot-parent 7 | 0.6.0-SNAPHOT 8 | 9 | 10 | 4.0.0 11 | wicket-pivot 12 | 0.6.0-SNAPHOT 13 | jar 14 | Wicket Pivot 15 | Simple web pivot table using Apache Wicket 16 | 17 | 18 | 2.8.9 19 | 20 | 21 | 22 | 23 | 24 | commons-collections 25 | commons-collections 26 | 3.2.2 27 | 28 | 29 | 30 | commons-lang 31 | commons-lang 32 | 2.6 33 | 34 | 35 | 36 | 37 | com.google.code.gson 38 | gson 39 | ${gson.version} 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/PivotDataSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | 17 | /** 18 | * @author Decebal Suiu 19 | */ 20 | public interface PivotDataSource extends Serializable { 21 | 22 | public String getFieldName(int fieldIndex); 23 | 24 | public int getFieldIndex(String fieldName); 25 | 26 | public Class getFieldType(int fieldIndex); 27 | 28 | /** 29 | * Gets number of fields in this data source. 30 | */ 31 | public int getFieldCount(); 32 | 33 | /** 34 | * Gets the row count. 35 | */ 36 | public int getRowCount(); 37 | 38 | /** 39 | * Gets the value at the specified field index and the row index. 40 | */ 41 | public Object getValueAt(int rowIndex, int fieldIndex); 42 | 43 | public Object getValueAt(int rowIndex, PivotField field); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/res/pivot.css: -------------------------------------------------------------------------------- 1 | @CHARSET "ISO-8859-1"; 2 | 3 | .pivot { 4 | margin-top: 10px; 5 | } 6 | 7 | .pivot .row-header { 8 | font-weight: bold; 9 | } 10 | 11 | .pivot .grand-total { 12 | color: #3A87AD; 13 | font-weight: bold; 14 | } 15 | 16 | .areas { 17 | padding: 5px; 18 | margin: 0px 0px 10px 0px; 19 | border: 1px solid #eee; 20 | -webkit-border-radius: 4px; 21 | -moz-border-radius: 4px; 22 | border-radius: 4px; 23 | } 24 | 25 | .area .name { 26 | font-weight: bold; 27 | padding: 5px; 28 | } 29 | 30 | .area .field { 31 | float: left; 32 | padding: 5px; 33 | margin-right: 5px; 34 | border: 1px solid #eee; 35 | background-color: #336699; 36 | -webkit-border-radius: 4px; 37 | -moz-border-radius: 4px; 38 | border-radius: 4px; 39 | font-size: 11px; 40 | font-weight: bold; 41 | line-height: 14px; 42 | color: #ffffff; 43 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 44 | white-space: nowrap; 45 | vertical-align: baseline; 46 | } 47 | 48 | .area .field-number { 49 | background-color: #669966; 50 | } 51 | 52 | .area ol.fields { 53 | list-style-type: none; 54 | float: left; 55 | display: block; 56 | } 57 | 58 | .pivot-placeholder { 59 | float: left; 60 | padding: 5px; 61 | margin-right: 5px; 62 | border: 1px dashed #ddd; 63 | background-color: #f0f0f0; 64 | -webkit-border-radius: 4px; 65 | -moz-border-radius: 4px; 66 | border-radius: 4px; 67 | line-height: 14px; 68 | white-space: nowrap; 69 | vertical-align: baseline; 70 | } 71 | 72 | .pivot-config-store-button { 73 | float:right; 74 | } -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/exporter/PivotExporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.exporter; 14 | 15 | import java.io.IOException; 16 | import java.io.OutputStream; 17 | import java.io.Serializable; 18 | 19 | import ro.fortsoft.wicket.pivot.PivotModel; 20 | 21 | /** 22 | * Interface for the pivot exporter plugins 23 | */ 24 | public interface PivotExporter extends Serializable { 25 | /** 26 | * @return how is this export named? E.g. CSV 27 | */ 28 | String getFormatName(); 29 | 30 | /** 31 | * @return the filename extension for the download without the ".", e.g. csv 32 | * or xls 33 | */ 34 | String getFilenameExtension(); 35 | 36 | /** 37 | * 38 | * @return the mimetype 39 | */ 40 | String getFormatMimetype(); 41 | 42 | /** 43 | * Export the given PivotModel into the outputStream. 44 | */ 45 | void exportPivot(PivotModel pivotModel, OutputStream outputStream) throws IOException; 46 | } 47 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/config/IPivotConfigStorage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013, 2014 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.config; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * Implementations of this interface can store and restore the current 19 | * configuration of the pivot table. 20 | * 21 | * This interface should be implemented by clients. 22 | */ 23 | public interface IPivotConfigStorage { 24 | /** 25 | * 26 | * @return the list of all stored configurations 27 | */ 28 | List listConfigNames(); 29 | 30 | /** 31 | * Load a configuration 32 | * 33 | * @param name 34 | * @return null or the stored configuration 35 | */ 36 | PivotConfig loadConfig(String name); 37 | 38 | /** 39 | * Save a configuration. If a configuration with the same name already 40 | * exists, it will be overwritten 41 | */ 42 | void saveConfig(PivotConfig config); 43 | 44 | /** 45 | * Delete the configuration with this name 46 | * 47 | * @param name 48 | */ 49 | void deleteConfig(String name); 50 | } 51 | -------------------------------------------------------------------------------- /demo/src/main/java/ro/fortsoft/wicket/pivot/demo/PivotApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.demo; 14 | 15 | import org.apache.wicket.Page; 16 | import org.apache.wicket.protocol.http.WebApplication; 17 | 18 | /** 19 | * @author Decebal Suiu 20 | */ 21 | public class PivotApplication extends WebApplication { 22 | 23 | public PivotApplication() { 24 | super(); 25 | } 26 | 27 | @Override 28 | public void init() { 29 | super.init(); 30 | 31 | // markup settings 32 | getMarkupSettings().setStripWicketTags(true); 33 | getMarkupSettings().setDefaultMarkupEncoding("UTF-8"); 34 | 35 | // exception settings 36 | getResourceSettings().setThrowExceptionOnMissingResource(false); 37 | } 38 | 39 | @Override 40 | public Class getHomePage() { 41 | return PivotPage.class; 42 | } 43 | 44 | /* 45 | // for test locale 46 | @Override 47 | public Session newSession(Request request, Response response) { 48 | Session session = super.newSession(request, response); 49 | session.setLocale(new Locale("ro", "RO")); 50 | 51 | return session; 52 | } 53 | */ 54 | 55 | } 56 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/DefaultPivotFieldActionsFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * @author Decebal Suiu 21 | */ 22 | public class DefaultPivotFieldActionsFactory implements PivotFieldActionsFactory, Serializable { 23 | 24 | private static final long serialVersionUID = 1L; 25 | 26 | @Override 27 | public List createPivotFieldActions(PivotField field, PivotModel model) { 28 | List fieldActions = new ArrayList<>(); 29 | 30 | // fieldActions.add(new PivotFieldAction.Delete(field)); 31 | if (field.getArea().equals(PivotField.Area.DATA) && field.getAggregator() != null) { 32 | fieldActions.add(new PivotFieldAction.AggregatorAction(field)); 33 | } 34 | if (field.getArea().equals(PivotField.Area.DATA)) { 35 | if (field.getFieldCalculation() != null) 36 | fieldActions.add(new PivotFieldAction.FieldCalculationAction(field, model)); 37 | else 38 | fieldActions.add(new PivotFieldAction.AddNewFieldCalculationAction(field, model)); 39 | } 40 | return fieldActions; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotPanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Load/Save Configuration 6 |
7 |
8 |
9 |
10 |
11 | 15 |     16 | 20 |     21 | 25 |
26 |
27 | 28 | Show pivot 29 | 30 |
31 |
32 |
33 | 34 |
35 | 36 |
37 |
38 |
39 | 40 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/tree/TreeIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.tree; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Iterator; 17 | import java.util.List; 18 | 19 | /** 20 | * @author Decebal Suiu 21 | */ 22 | public class TreeIterator implements Iterator { 23 | 24 | private Iterator iterator; 25 | 26 | public TreeIterator(Node root) { 27 | this.iterator = getList(root).iterator(); 28 | } 29 | 30 | @Override 31 | public boolean hasNext() { 32 | return iterator.hasNext(); 33 | } 34 | 35 | @Override 36 | public void remove() { 37 | throw new UnsupportedOperationException(); 38 | } 39 | 40 | @Override 41 | public Node next() { 42 | return iterator.next(); 43 | } 44 | 45 | public Iterator getValuesIterator() { 46 | List values = new ArrayList<>(); 47 | while (hasNext()) { 48 | values.add(next().getData()); 49 | } 50 | 51 | return values.iterator(); 52 | } 53 | 54 | private List getList(Node node) { 55 | List nodes = new ArrayList<>(); 56 | nodes.add(node); 57 | for (Node value : node.getChildren()) { 58 | nodes.addAll(getList(value)); 59 | } 60 | 61 | return nodes; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/PivotModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | import java.util.List; 17 | 18 | import ro.fortsoft.wicket.pivot.PivotField.Area; 19 | import ro.fortsoft.wicket.pivot.tree.Tree; 20 | 21 | /** 22 | * @author Decebal Suiu 23 | */ 24 | public interface PivotModel extends Serializable { 25 | 26 | /** 27 | * Gets all the PivotFields. 28 | */ 29 | public List getFields(); 30 | 31 | public List getFields(Area area); 32 | 33 | public PivotField getField(String name); 34 | 35 | public PivotField getField(int index); 36 | 37 | public PivotDataSource getDataSource(); 38 | 39 | /** 40 | * Calculates the pivot data. 41 | */ 42 | public void calculate(); 43 | 44 | public List> getRowKeys(); 45 | 46 | public List> getColumnKeys(); 47 | 48 | public Object getValueAt(PivotField dataField, List rowKey, List columnKey); 49 | 50 | public boolean isShowGrandTotalForColumn(); 51 | 52 | public void setShowGrandTotalForColumn(boolean showGrandTotalForColumn); 53 | 54 | public boolean isShowGrandTotalForRow(); 55 | 56 | public void setShowGrandTotalForRow(boolean showGrandTotalForRow); 57 | 58 | public Tree getColumnsHeaderTree(); // ?! 59 | 60 | public Tree getRowsHeaderTree(); // ?! 61 | 62 | public boolean isAutoCalculate(); 63 | 64 | public void setAutoCalculate(boolean autoCalculate); 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotResourcesBehavior.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import org.apache.wicket.Component; 16 | import org.apache.wicket.behavior.Behavior; 17 | import org.apache.wicket.markup.head.CssHeaderItem; 18 | import org.apache.wicket.markup.head.IHeaderResponse; 19 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 20 | 21 | /** 22 | * @author Decebal Suiu 23 | */ 24 | public class PivotResourcesBehavior extends Behavior { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | @Override 29 | public void renderHead(Component component, IHeaderResponse response) { 30 | super.renderHead(component, response); 31 | 32 | PivotSettings settings = PivotSettings.get(); 33 | 34 | if (settings.isIncludeJQuery()) { 35 | response.render(JavaScriptHeaderItem.forReference(settings.getJQueryReference())); 36 | } 37 | 38 | if (settings.isIncludeJQueryUI()) { 39 | response.render(JavaScriptHeaderItem.forReference(settings.getJQueryUIReference())); 40 | } 41 | 42 | if (settings.isIncludeJQueryJson()) { 43 | response.render(JavaScriptHeaderItem.forReference(settings.getJQueryJsonReference())); 44 | } 45 | 46 | if (settings.isIncludeJavaScript()) { 47 | response.render(JavaScriptHeaderItem.forReference(settings.getJavaScriptReference())); 48 | } 49 | 50 | if (settings.isIncludeCss()) { 51 | response.render(CssHeaderItem.forReference(settings.getCssReference())); 52 | } 53 | 54 | if (settings.isIncludeBootstrap()) { 55 | response.render(CssHeaderItem.forReference(settings.getBootstrapCssReference())); 56 | response.render(JavaScriptHeaderItem.forReference(settings.getBootstrapJavaScriptReference())); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/res/jquery.json-2.2.min.js: -------------------------------------------------------------------------------- 1 | 2 | (function($){$.toJSON=function(o) 3 | {if(typeof(JSON)=='object'&&JSON.stringify) 4 | return JSON.stringify(o);var type=typeof(o);if(o===null) 5 | return"null";if(type=="undefined") 6 | return undefined;if(type=="number"||type=="boolean") 7 | return o+"";if(type=="string") 8 | return $.quoteString(o);if(type=='object') 9 | {if(typeof o.toJSON=="function") 10 | return $.toJSON(o.toJSON());if(o.constructor===Date) 11 | {var month=o.getUTCMonth()+1;if(month<10)month='0'+month;var day=o.getUTCDate();if(day<10)day='0'+day;var year=o.getUTCFullYear();var hours=o.getUTCHours();if(hours<10)hours='0'+hours;var minutes=o.getUTCMinutes();if(minutes<10)minutes='0'+minutes;var seconds=o.getUTCSeconds();if(seconds<10)seconds='0'+seconds;var milli=o.getUTCMilliseconds();if(milli<100)milli='0'+milli;if(milli<10)milli='0'+milli;return'"'+year+'-'+month+'-'+day+'T'+ 12 | hours+':'+minutes+':'+seconds+'.'+milli+'Z"';} 13 | if(o.constructor===Array) 14 | {var ret=[];for(var i=0;i listConfigNames() { 35 | @SuppressWarnings("unchecked") 36 | Map map = getStorageMap(); 37 | return new ArrayList<>(map.keySet()); 38 | } 39 | 40 | private Map getStorageMap() { 41 | @SuppressWarnings("unchecked") 42 | HashMap map = (HashMap) Session.get().getAttribute("PivotConfigStorageSession"); 43 | if (map == null) { 44 | map = new HashMap<>(); 45 | Session.get().setAttribute("PivotConfigStorageSession", map); 46 | } 47 | return map; 48 | } 49 | 50 | @Override 51 | public PivotConfig loadConfig(String name) { 52 | String str = getStorageMap().get(name); 53 | if (str == null) 54 | return null; 55 | return new Gson().fromJson(str, PivotConfig.class); 56 | } 57 | 58 | @Override 59 | public void saveConfig(PivotConfig config) { 60 | getStorageMap().put(config.getName(), new Gson().toJson(config)); 61 | } 62 | 63 | @Override 64 | public void deleteConfig(String name) { 65 | getStorageMap().remove(name); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/SortableAjaxBehavior.java: -------------------------------------------------------------------------------- 1 | package ro.fortsoft.wicket.pivot.web; 2 | 3 | import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; 4 | import org.apache.wicket.ajax.AjaxRequestTarget; 5 | import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; 6 | 7 | import com.google.gson.Gson; 8 | 9 | /** 10 | * @author Decebal Suiu 11 | */ 12 | public abstract class SortableAjaxBehavior extends AbstractDefaultAjaxBehavior { 13 | 14 | private static final long serialVersionUID = 1L; 15 | 16 | /** Sorted identifiant into the request */ 17 | private static final String JSON_DATA = "data"; 18 | 19 | public abstract void onSort(AjaxRequestTarget target, Item[] items); 20 | 21 | @Override 22 | protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { 23 | super.updateAjaxAttributes(attributes); 24 | 25 | // String areasId = getComponent().findParent(PivotPanel.class).get("areas").getMarkupId(); 26 | // System.out.println("areasId = " + areasId); 27 | 28 | StringBuilder buffer = new StringBuilder(); 29 | // buffer.append("var data = serializeFieldLocations(" + areasId + ");"); 30 | buffer.append("var data = serializeFieldLocations();"); 31 | buffer.append("return {'" + JSON_DATA + "': data};"); 32 | 33 | attributes.getDynamicExtraParameters().add(buffer); 34 | } 35 | 36 | @Override 37 | protected void respond(AjaxRequestTarget target) { 38 | String jsonData = getComponent().getRequest().getRequestParameters().getParameterValue(JSON_DATA).toString(); 39 | Item[] items = getItems(jsonData); 40 | onSort(target, items); 41 | } 42 | 43 | private Item[] getItems(String jsonData) { 44 | Gson gson = new Gson(); 45 | Item[] items = gson.fromJson(jsonData, Item[].class); 46 | /* 47 | System.out.println(items.length); 48 | for (Item item : items) { 49 | System.out.println(item); 50 | } 51 | */ 52 | 53 | return items; 54 | } 55 | 56 | public static class Item { 57 | 58 | public String areaName; 59 | public int fieldIndex; 60 | public int sortIndex; 61 | 62 | @Override 63 | public String toString() { 64 | StringBuilder buffer = new StringBuilder(); 65 | buffer.append("Item["); 66 | buffer.append("areaName = ").append(areaName); 67 | buffer.append(" fieldIndex = ").append(fieldIndex); 68 | buffer.append(" sortIndex = ").append(sortIndex); 69 | buffer.append("]"); 70 | 71 | return buffer.toString(); 72 | } 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/tree/TreeHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.tree; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Iterator; 17 | import java.util.List; 18 | import java.util.Objects; 19 | 20 | /** 21 | * @author Decebal Suiu 22 | */ 23 | public class TreeHelper { 24 | 25 | public static List getLeafs(Node root) { 26 | List leafs = new ArrayList<>(); 27 | Iterator it = new TreeIterator(root); 28 | while (it.hasNext()) { 29 | Node node = it.next(); 30 | if (node.isLeaf()) { 31 | leafs.add(node); 32 | } 33 | } 34 | 35 | return leafs; 36 | } 37 | 38 | public static List> getLeafValues(Node root) { 39 | List> leafValues = new ArrayList<>(); 40 | List leafs = getLeafs(root); 41 | for (Node leaf : leafs) { 42 | leafValues.add(leaf.getPathValues()); 43 | } 44 | 45 | return leafValues; 46 | } 47 | 48 | public static Node getNode(Node root, List pathValues) { 49 | Node node = root; 50 | for (Object value : pathValues) { 51 | node = getChild(node, value); 52 | } 53 | 54 | return node; 55 | } 56 | 57 | public static Node getChild(Node node, Object value) { 58 | for (Node child : node.getChildren()) { 59 | Object data = child.getData(); 60 | if (Objects.equals(value, data)) { 61 | return child; 62 | } 63 | } 64 | 65 | return null; 66 | } 67 | 68 | 69 | public static void printTree(Node root) { 70 | Iterator it = new TreeIterator(root); 71 | while (it.hasNext()) { 72 | Node node = it.next(); 73 | int level = node.getLevel(); 74 | if (level == 0) { 75 | System.out.println(String.format(node.toString() + " [%s]", getLeafs(node).size())); 76 | } else { 77 | System.out.println(String.format("%" + 3 * level + "s " + node.toString() + " [%s]", " ", getLeafs(node).size())); 78 | } 79 | } 80 | } 81 | 82 | public static void printLeafValues(Node root) { 83 | List> leafValues = getLeafValues(root); 84 | System.out.println("count > " + leafValues.size()); 85 | for (List values : leafValues) { 86 | System.out.println(values); 87 | } 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /demo/src/test/java/ro/fortsoft/wicket/pivot/Start.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import org.eclipse.jetty.server.Server; 16 | import org.eclipse.jetty.server.bio.SocketConnector; 17 | import org.eclipse.jetty.webapp.WebAppContext; 18 | 19 | /** 20 | * @author Decebal Suiu 21 | */ 22 | public class Start { 23 | 24 | public static void main(String[] args) throws Exception { 25 | Server server = new Server(); 26 | SocketConnector connector = new SocketConnector(); 27 | 28 | // Set some timeout options to make debugging easier. 29 | connector.setMaxIdleTime(1000 * 60 * 60); 30 | connector.setSoLingerTime(-1); 31 | int port = Integer.parseInt(System.getProperty("jetty.port", "8081")); 32 | connector.setPort(port); 33 | server.addConnector(connector); 34 | 35 | // the orders of handlers is very important! 36 | /* 37 | ContextHandler contextHandler = new ContextHandler(); 38 | contextHandler.setContextPath("/res"); 39 | contextHandler.setResourceBase("./upload/images/"); 40 | contextHandler.addHandler(new ResourceHandler()); 41 | 42 | server.addHandler(contextHandler); 43 | */ 44 | 45 | WebAppContext webAppContext = new WebAppContext(); 46 | webAppContext.setServer(server); 47 | webAppContext.setContextPath("/"); 48 | webAppContext.setWar("src/main/webapp"); 49 | 50 | // add virtual host but DON"T FORGET to add stores with these hosts 51 | // String[] virtualHosts = { "127.0.0.1", "127.0.0.2" }; 52 | // webAppContext.setVirtualHosts(virtualHosts); 53 | 54 | server.setHandler(webAppContext); 55 | 56 | // START JMX SERVER 57 | // MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); 58 | // MBeanContainer mBeanContainer = new MBeanContainer(mBeanServer); 59 | // server.getContainer().addEventListener(mBeanContainer); 60 | // mBeanContainer.start(); 61 | 62 | try { 63 | System.out.println(">>> STARTING EMBEDDED JETTY SERVER (" + port + "), PRESS ANY KEY TO STOP"); 64 | server.start(); 65 | System.in.read(); 66 | System.out.println(">>> STOPPING EMBEDDED JETTY SERVER"); 67 | // while (System.in.available() == 0) { 68 | // Thread.sleep(5000); 69 | // } 70 | server.stop(); 71 | server.join(); 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | System.exit(100); 75 | } 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /demo/src/main/java/ro/fortsoft/wicket/pivot/demo/PivotPage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.demo; 14 | 15 | import org.apache.wicket.markup.html.WebPage; 16 | 17 | import ro.fortsoft.wicket.pivot.PivotDataSource; 18 | import ro.fortsoft.wicket.pivot.PivotField; 19 | import ro.fortsoft.wicket.pivot.PivotModel; 20 | import ro.fortsoft.wicket.pivot.web.PivotPanel; 21 | 22 | /** 23 | * @author Decebal Suiu 24 | */ 25 | public class PivotPage extends WebPage { 26 | 27 | private static final long serialVersionUID = 1L; 28 | 29 | public PivotPage() { 30 | super(); 31 | 32 | // create a pivot data source 33 | PivotDataSource pivotDataSource = PivotDataSourceHandler.getPivotDataSource(); 34 | System.out.println("pivotDataSource = " + pivotDataSource); 35 | // System.out.println("fieldCount = " + pivotDataSource.getFieldCount()); 36 | // System.out.println("rowCount = " + pivotDataSource.getRowCount()); 37 | 38 | PivotPanel pivotPanel = new PivotPanel("pivot", pivotDataSource) { 39 | 40 | private static final long serialVersionUID = 1L; 41 | 42 | @Override 43 | protected PivotModel createPivotModel(PivotDataSource pivotDataSource) { 44 | PivotModel pivotModel = super.createPivotModel(pivotDataSource); 45 | 46 | // add some fields on some area 47 | pivotModel.getField("REGION").setArea(PivotField.Area.ROW); 48 | pivotModel.getField("SALESMAN").setArea(PivotField.Area.ROW).setAreaIndex(1); 49 | pivotModel.getField("YEAR").setArea(PivotField.Area.COLUMN); 50 | pivotModel.getField("MONTH").setArea(PivotField.Area.COLUMN).setAreaIndex(1); 51 | pivotModel.getField("SALES").setArea(PivotField.Area.DATA); 52 | 53 | // set an aggregator for a data pivot field 54 | // pivotModel.getField("SALES").setAggregator(new Aggregator.Count()); 55 | 56 | // set a custom converter for a pivot field 57 | /* 58 | pivotModel.getField("SALES").setConverter(new DoubleConverter() { 59 | 60 | private static final long serialVersionUID = 1L; 61 | 62 | @Override 63 | public NumberFormat getNumberFormat(Locale locale) { 64 | NumberFormat format = super.getNumberFormat(locale); 65 | format.setMinimumFractionDigits(2); 66 | 67 | return format; 68 | } 69 | 70 | }); 71 | */ 72 | 73 | // show grand totals 74 | pivotModel.setShowGrandTotalForColumn(true); 75 | pivotModel.setShowGrandTotalForRow(true); 76 | 77 | return pivotModel; 78 | } 79 | 80 | }; 81 | pivotPanel.setPivotConfigStorage(new PivotConfigSessionStorage()); 82 | add(pivotPanel); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/res/jquery.dropdown.css: -------------------------------------------------------------------------------- 1 | .jquery-dropdown { 2 | position: absolute; 3 | z-index: 9999999; 4 | display: none; 5 | } 6 | 7 | .jquery-dropdown .jquery-dropdown-menu, 8 | .jquery-dropdown .dropdown-panel { 9 | min-width: 160px; 10 | max-width: 360px; 11 | list-style: none; 12 | background: #FFF; 13 | border: solid 1px #DDD; 14 | border: solid 1px rgba(0, 0, 0, .2); 15 | border-radius: 6px; 16 | box-shadow: 0 5px 10px rgba(0, 0, 0, .2); 17 | overflow: visible; 18 | padding: 4px 0; 19 | margin: 0; 20 | text-shadow: none; 21 | font-size: 13px; 22 | font-weight: normal; 23 | } 24 | 25 | .jquery-dropdown .dropdown-panel { 26 | padding: 10px; 27 | } 28 | 29 | .jquery-dropdown.dropdown-tip { 30 | margin-top: 8px; 31 | } 32 | 33 | .jquery-dropdown.dropdown-tip:before { 34 | position: absolute; 35 | top: -6px; 36 | left: 9px; 37 | content: ''; 38 | border-left: 7px solid transparent; 39 | border-right: 7px solid transparent; 40 | border-bottom: 7px solid #CCC; 41 | border-bottom-color: rgba(0, 0, 0, 0.2); 42 | display: inline-block; 43 | } 44 | 45 | .jquery-dropdown.dropdown-tip.dropdown-anchor-right:before { 46 | left: auto; 47 | right: 9px; 48 | } 49 | 50 | .jquery-dropdown.dropdown-tip:after { 51 | position: absolute; 52 | top: -5px; 53 | left: 10px; 54 | content: ''; 55 | border-left: 6px solid transparent; 56 | border-right: 6px solid transparent; 57 | border-bottom: 6px solid #FFF; 58 | display: inline-block; 59 | } 60 | 61 | .jquery-dropdown.dropdown-tip.dropdown-anchor-right:after { 62 | left: auto; 63 | right: 10px; 64 | } 65 | 66 | 67 | .jquery-dropdown.dropdown-scroll .jquery-dropdown-menu, 68 | .jquery-dropdown.dropdown-scroll .dropdown-panel { 69 | max-height: 358px; 70 | overflow: auto; 71 | } 72 | 73 | .jquery-dropdown .jquery-dropdown-menu LI { 74 | list-style: none; 75 | padding: 0 0; 76 | margin: 0; 77 | line-height: 18px; 78 | } 79 | 80 | .jquery-dropdown .jquery-dropdown-menu LI > A, 81 | .jquery-dropdown .jquery-dropdown-menu LABEL { 82 | display: block; 83 | color: #555; 84 | text-decoration: none; 85 | line-height: 18px; 86 | padding: 3px 15px; 87 | white-space: nowrap; 88 | } 89 | 90 | .jquery-dropdown .jquery-dropdown-menu LI > A:hover, 91 | .jquery-dropdown .jquery-dropdown-menu LABEL:hover { 92 | background-color: #08C; 93 | color: #FFF; 94 | cursor: pointer; 95 | } 96 | 97 | .jquery-dropdown .jquery-dropdown-menu .dropdown-divider { 98 | font-size: 1px; 99 | border-top: solid 1px #E5E5E5; 100 | padding: 0; 101 | margin: 5px 0; 102 | } 103 | 104 | /* Icon Examples - icons courtesy of http://p.yusukekamiyamane.com/ */ 105 | .jquery-dropdown.has-icons LI > A { 106 | padding-left: 30px; 107 | background-position: 8px center; 108 | background-repeat: no-repeat; 109 | } 110 | 111 | .jquery-dropdown .undo A { background-image: url(icons/arrow-curve-180-left.png); } 112 | .jquery-dropdown .redo A { background-image: url(icons/arrow-curve.png); } 113 | .jquery-dropdown .cut A { background-image: url(icons/scissors.png); } 114 | .jquery-dropdown .copy A { background-image: url(icons/document-copy.png); } 115 | .jquery-dropdown .paste A { background-image: url(icons/clipboard.png); } 116 | .jquery-dropdown .delete A { background-image: url(icons/cross-script.png); } -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/exporter/PivotCsvExporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.exporter; 14 | 15 | import java.io.IOException; 16 | import java.io.OutputStream; 17 | import java.io.OutputStreamWriter; 18 | import java.nio.charset.Charset; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | import ro.fortsoft.wicket.pivot.PivotModel; 23 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel; 24 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.RenderCell; 25 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.RenderRow; 26 | 27 | /** 28 | * Basic CSV exporter 29 | */ 30 | public class PivotCsvExporter implements PivotExporter { 31 | private static final long serialVersionUID = 1L; 32 | private String seperator = ";"; 33 | 34 | @Override 35 | public void exportPivot(PivotModel pivotModel, OutputStream outputStream) throws IOException { 36 | PivotTableRenderModel renderModel = PivotTableRenderModel.create(pivotModel); 37 | 38 | OutputStreamWriter out = new OutputStreamWriter(outputStream, Charset.forName("UTF-8")); 39 | Map rowSpanMap = new HashMap<>(); 40 | 41 | for (RenderRow row : renderModel.getAllRenderRows()) { 42 | int col = 0; 43 | for (RenderCell cell : row.getRenderCells()) { 44 | /* 45 | * Check if we currently have a rowspan at this column from the 46 | * parent row. We only support a colspan of 1 at the moment 47 | * here, if rowspan > 1 48 | */ 49 | Integer rowSpan = rowSpanMap.get(col); 50 | if (rowSpan != null) { 51 | rowSpan--; 52 | if (rowSpan == 0) 53 | rowSpanMap.remove(col); 54 | else 55 | rowSpanMap.put(col, rowSpan); 56 | col++; 57 | out.append(seperator); 58 | } 59 | 60 | /* 61 | * Output the Value 62 | */ 63 | Object rawValue = cell.getRawValue(); 64 | if (rawValue != null) 65 | out.append(String.valueOf(rawValue)); 66 | out.append(seperator); 67 | if (cell.getRowspan() > 1) 68 | rowSpanMap.put(col, cell.getRowspan() - 1); 69 | col++; 70 | 71 | /* 72 | * We only support colspan _OR_ rowspan. The current PivotTable 73 | * also doesnt have rowspan and colspan at the same time. 74 | */ 75 | for (int i = 1; i < cell.getColspan(); i++) { 76 | out.append(seperator); 77 | col++; 78 | } 79 | } 80 | out.append("\n"); 81 | } 82 | out.flush(); 83 | } 84 | 85 | public void setSeperator(String seperator) { 86 | this.seperator = seperator; 87 | } 88 | 89 | @Override 90 | public String getFormatName() { 91 | return "CSV"; 92 | } 93 | 94 | @Override 95 | public String getFormatMimetype() { 96 | return "text/csv"; 97 | } 98 | 99 | @Override 100 | public String getFilenameExtension() { 101 | return ".csv"; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/AggregatorPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import org.apache.wicket.ajax.AjaxRequestTarget; 19 | import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; 20 | import org.apache.wicket.ajax.markup.html.AjaxLink; 21 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 22 | import org.apache.wicket.markup.html.form.ChoiceRenderer; 23 | import org.apache.wicket.markup.html.form.DropDownChoice; 24 | import org.apache.wicket.markup.html.panel.GenericPanel; 25 | import org.apache.wicket.model.IModel; 26 | import org.apache.wicket.model.PropertyModel; 27 | 28 | import ro.fortsoft.wicket.pivot.Aggregator; 29 | import ro.fortsoft.wicket.pivot.PivotField; 30 | 31 | /** 32 | * @author Decebal Suiu 33 | */ 34 | public class AggregatorPanel extends GenericPanel { 35 | 36 | private static final long serialVersionUID = 1L; 37 | 38 | private Aggregator aggregator; 39 | private boolean okPressed; 40 | 41 | public AggregatorPanel(String id, IModel model) { 42 | super(id, model); 43 | 44 | okPressed = false; 45 | 46 | aggregator = getModelObject().getAggregator(); 47 | 48 | List aggregators = new ArrayList<>(); 49 | aggregators.add(Aggregator.get(Aggregator.SUM)); 50 | aggregators.add(Aggregator.get(Aggregator.AVG)); 51 | aggregators.add(Aggregator.get(Aggregator.MIN)); 52 | aggregators.add(Aggregator.get(Aggregator.MAX)); 53 | aggregators.add(Aggregator.get(Aggregator.COUNT)); 54 | final DropDownChoice aggregatorDownChoice = new DropDownChoice<>("aggregator", 55 | new PropertyModel<>(this, "aggregator"), aggregators, 56 | new ChoiceRenderer("function") { 57 | 58 | private static final long serialVersionUID = 1L; 59 | 60 | @Override 61 | public Object getDisplayValue(Aggregator object) { 62 | return ((String) super.getDisplayValue(object)).toUpperCase(); 63 | } 64 | 65 | }); 66 | aggregatorDownChoice.add(new OnChangeAjaxBehavior() { 67 | 68 | private static final long serialVersionUID = 1L; 69 | 70 | @Override 71 | protected void onUpdate(AjaxRequestTarget target) { 72 | } 73 | 74 | }); 75 | aggregatorDownChoice.setOutputMarkupId(true); 76 | add(aggregatorDownChoice); 77 | 78 | add(new AjaxLink("ok") { 79 | 80 | private static final long serialVersionUID = 1L; 81 | 82 | @Override 83 | public void onClick(AjaxRequestTarget target) { 84 | okPressed = true; 85 | ModalWindow.closeCurrent(target); 86 | 87 | } 88 | 89 | }); 90 | } 91 | 92 | public boolean isOkPressed() { 93 | return okPressed; 94 | } 95 | 96 | public Aggregator getAggregator() { 97 | return aggregator; 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotConfigStoragePanel.java: -------------------------------------------------------------------------------- 1 | package ro.fortsoft.wicket.pivot.web; 2 | 3 | import org.apache.wicket.ajax.AjaxRequestTarget; 4 | import org.apache.wicket.ajax.markup.html.AjaxLink; 5 | import org.apache.wicket.ajax.markup.html.form.AjaxButton; 6 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 7 | import org.apache.wicket.markup.html.basic.Label; 8 | import org.apache.wicket.markup.html.form.Form; 9 | import org.apache.wicket.markup.html.form.TextField; 10 | import org.apache.wicket.markup.html.list.ListItem; 11 | import org.apache.wicket.markup.html.list.ListView; 12 | import org.apache.wicket.markup.html.panel.Panel; 13 | import org.apache.wicket.model.Model; 14 | import ro.fortsoft.wicket.pivot.PivotModel; 15 | import ro.fortsoft.wicket.pivot.config.IPivotConfigStorage; 16 | import ro.fortsoft.wicket.pivot.config.PivotConfig; 17 | 18 | import java.util.ArrayList; 19 | 20 | public class PivotConfigStoragePanel extends Panel { 21 | private static final long serialVersionUID = 1L; 22 | private IPivotConfigStorage pivotConfigStorage; 23 | private Model> configNameModel; 24 | private ListView configListView; 25 | 26 | public PivotConfigStoragePanel(String id, final PivotModel pivotModel, final IPivotConfigStorage pivotConfigStorage) { 27 | super(id); 28 | this.pivotConfigStorage = pivotConfigStorage; 29 | 30 | Form form = new Form<>("form"); 31 | final TextField configStoreName = new TextField<>("configStoreName", new Model<>("")); 32 | form.add(configStoreName); 33 | 34 | AjaxButton saveButton = new AjaxButton("save") { 35 | private static final long serialVersionUID = 1L; 36 | 37 | @Override 38 | protected void onSubmit(AjaxRequestTarget target) { 39 | String value = configStoreName.getValue(); 40 | if (value == null || value.isEmpty()) 41 | return; 42 | PivotConfig config = new PivotConfig(); 43 | config.setName(value); 44 | config.storeModelState(pivotModel); 45 | pivotConfigStorage.saveConfig(config); 46 | ModalWindow.closeCurrent(target); 47 | } 48 | }; 49 | form.add(saveButton); 50 | add(form); 51 | 52 | configNameModel = new Model<>(); 53 | configListView = new ListView("configs", configNameModel) { 54 | private static final long serialVersionUID = 1L; 55 | 56 | @Override 57 | protected void populateItem(final ListItem item) { 58 | item.add(new Label("name", new Model<>(item.getModelObject()))); 59 | item.add(new AjaxLink("load") { 60 | private static final long serialVersionUID = 1L; 61 | 62 | @Override 63 | public void onClick(AjaxRequestTarget target) { 64 | PivotConfig config = pivotConfigStorage.loadConfig(item.getModelObject()); 65 | if (config == null) { 66 | refreshList(target); 67 | return; 68 | } 69 | config.restoreModelState(pivotModel); 70 | ModalWindow.closeCurrent(target); 71 | } 72 | }); 73 | item.add(new AjaxLink("delete") { 74 | private static final long serialVersionUID = 1L; 75 | 76 | @Override 77 | public void onClick(AjaxRequestTarget target) { 78 | pivotConfigStorage.deleteConfig(item.getModelObject()); 79 | refreshList(target); 80 | } 81 | }); 82 | } 83 | }; 84 | setOutputMarkupId(true); 85 | add(configListView); 86 | refreshList(null); 87 | } 88 | 89 | private void refreshList(AjaxRequestTarget target) { 90 | configNameModel.setObject(new ArrayList<>(pivotConfigStorage.listConfigNames())); 91 | if (target != null) 92 | target.add(this); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/ResultSetPivotDataSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.sql.ResultSet; 16 | import java.sql.ResultSetMetaData; 17 | import java.sql.SQLException; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Decebal Suiu 23 | */ 24 | public class ResultSetPivotDataSource implements PivotDataSource { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | private List> data; 29 | private List columnNames; 30 | private List> columnTypes; 31 | private int rowCount; 32 | private int columnCount; 33 | 34 | public ResultSetPivotDataSource(ResultSet resultSet) throws SQLException { 35 | data = new ArrayList<>(); 36 | columnNames = new ArrayList<>(); 37 | columnTypes = new ArrayList<>(); 38 | 39 | populate(resultSet); 40 | } 41 | 42 | @Override 43 | public String getFieldName(int fieldIndex) { 44 | return columnNames.get(fieldIndex); 45 | } 46 | 47 | @Override 48 | public int getFieldIndex(String fieldName) { 49 | for (int i = 0; i < columnNames.size(); i++) { 50 | if (columnNames.get(i).equals(fieldName)) { 51 | return i; 52 | } 53 | } 54 | 55 | return -1; 56 | } 57 | 58 | @Override 59 | public Class getFieldType(int fieldIndex) { 60 | return columnTypes.get(fieldIndex); 61 | } 62 | 63 | @Override 64 | public int getFieldCount() { 65 | return columnCount; 66 | } 67 | 68 | @Override 69 | public int getRowCount() { 70 | return rowCount; 71 | } 72 | 73 | @Override 74 | public Object getValueAt(int rowIndex, int fieldIndex) { 75 | return data.get(rowIndex).get(fieldIndex); 76 | } 77 | 78 | @Override 79 | public Object getValueAt(int rowIndex, PivotField field) { 80 | int fieldIndex = getFieldIndex(field.getName()); 81 | return data.get(rowIndex).get(fieldIndex); 82 | } 83 | 84 | private void populate(ResultSet resultSet) throws SQLException { 85 | boolean firstRow = true; 86 | while (resultSet.next()) { 87 | if (firstRow) { 88 | firstRow = false; 89 | ResultSetMetaData resultSetMetaData = resultSet.getMetaData(); 90 | columnCount = resultSetMetaData.getColumnCount(); 91 | for (int i = 0; i < columnCount; i++) { 92 | columnNames.add(resultSetMetaData.getColumnLabel(i + 1)); 93 | try { 94 | columnTypes.add(Class.forName(resultSetMetaData.getColumnClassName(i + 1))); 95 | } catch (ClassNotFoundException e) { 96 | // TODO Auto-generated catch block 97 | e.printStackTrace(); 98 | } 99 | } 100 | // System.out.println(columnNames); 101 | // System.out.println(columnTypes); 102 | } 103 | 104 | List row = new ArrayList<>(columnCount); 105 | for (int i = 0; i < columnCount; i++) { 106 | row.add(resultSet.getObject(i + 1)); 107 | } 108 | data.add(row); 109 | 110 | rowCount++; 111 | } 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/tree/Node.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.tree; 14 | 15 | import java.io.Serializable; 16 | import java.util.ArrayList; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | /** 21 | * @author Decebal Suiu 22 | */ 23 | public class Node implements Serializable { 24 | 25 | private static final long serialVersionUID = 1L; 26 | 27 | private Node parent; 28 | private Object data; 29 | private List children; 30 | 31 | public Node() { 32 | children = new ArrayList<>(); 33 | } 34 | 35 | public Node(Object data) { 36 | this.data = data; 37 | children = new ArrayList<>(); 38 | } 39 | 40 | public Node getParent() { 41 | return parent; 42 | } 43 | 44 | public boolean isRoot() { 45 | return parent == null; 46 | } 47 | 48 | public int getLevel() { 49 | int level = 0; 50 | Node p = parent; 51 | while (p != null) { 52 | ++level; 53 | p = p.parent; 54 | } 55 | 56 | return level; 57 | } 58 | 59 | public boolean isLeaf() { 60 | return children.isEmpty(); 61 | } 62 | 63 | public Object getData() { 64 | return data; 65 | } 66 | 67 | public void setData(Object data) { 68 | this.data = data; 69 | } 70 | 71 | public List getChildren() { 72 | // return new ArrayList(children); /* return a shallow copy */ 73 | return children; 74 | } 75 | 76 | public void addChild(Node child) { 77 | child.parent = this; 78 | children.add(child); 79 | } 80 | 81 | public void insert(Object data) { 82 | addChild(new Node(data)); 83 | } 84 | 85 | public List getPathValues() { 86 | if (isRoot()) { 87 | return Collections.emptyList(); 88 | } 89 | 90 | List pathValues = new ArrayList<>(); 91 | pathValues.add(getData()); 92 | Node parent = getParent(); 93 | while (parent != null) { 94 | pathValues.add(parent.getData()); 95 | parent = parent.getParent(); 96 | } 97 | pathValues.remove(pathValues.size() - 1); // remove root 98 | Collections.reverse(pathValues); 99 | 100 | return pathValues; 101 | } 102 | 103 | @Override 104 | public int hashCode() { 105 | final int prime = 31; 106 | int result = 1; 107 | result = prime * result + ((data == null) ? 0 : data.hashCode()); 108 | return result; 109 | } 110 | 111 | @Override 112 | public boolean equals(Object obj) { 113 | if (this == obj) { 114 | return true; 115 | } 116 | 117 | if (obj == null) { 118 | return false; 119 | } 120 | 121 | if (getClass() != obj.getClass()) { 122 | return false; 123 | } 124 | 125 | Node other = (Node) obj; 126 | if (data == null) { 127 | if (other.data != null) { 128 | return false; 129 | } 130 | } else if (!data.equals(other.data)) { 131 | return false; 132 | } 133 | 134 | return true; 135 | } 136 | 137 | @Override 138 | public String toString() { 139 | return (data == null) ? "null" : data.toString(); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotFieldActionsPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import java.util.List; 16 | 17 | import org.apache.wicket.markup.head.CssHeaderItem; 18 | import org.apache.wicket.markup.head.IHeaderResponse; 19 | import org.apache.wicket.markup.head.JavaScriptHeaderItem; 20 | import org.apache.wicket.markup.html.WebMarkupContainer; 21 | import org.apache.wicket.markup.html.basic.Label; 22 | import org.apache.wicket.markup.html.link.AbstractLink; 23 | import org.apache.wicket.markup.html.list.ListItem; 24 | import org.apache.wicket.markup.html.list.ListView; 25 | import org.apache.wicket.markup.html.panel.GenericPanel; 26 | import org.apache.wicket.model.IModel; 27 | import org.apache.wicket.model.LoadableDetachableModel; 28 | import org.apache.wicket.request.resource.PackageResourceReference; 29 | import org.apache.wicket.resource.JQueryPluginResourceReference; 30 | 31 | import ro.fortsoft.wicket.pivot.PivotField; 32 | import ro.fortsoft.wicket.pivot.PivotFieldAction; 33 | import ro.fortsoft.wicket.pivot.PivotModel; 34 | 35 | /** 36 | * @author Decebal Suiu 37 | */ 38 | public class PivotFieldActionsPanel extends GenericPanel { 39 | 40 | private static final long serialVersionUID = 1L; 41 | 42 | private IModel> actionsModel; 43 | 44 | 45 | public PivotFieldActionsPanel(String id, IModel model, final IModel pivotModel) { 46 | super(id, model); 47 | 48 | WebMarkupContainer dropDownContainer = new WebMarkupContainer("dropdown"); 49 | dropDownContainer.setOutputMarkupId(true); 50 | add(dropDownContainer); 51 | 52 | actionsModel = new LoadableDetachableModel>() { 53 | 54 | private static final long serialVersionUID = 1L; 55 | 56 | @Override 57 | protected List load() { 58 | PivotPanel pivotPanel = findParent(PivotPanel.class); 59 | return pivotPanel.getPivotFieldActionsFactory().createPivotFieldActions(getPivotField(), pivotModel.getObject()); 60 | } 61 | 62 | }; 63 | ListView actionsView = new ListView("action", actionsModel) { 64 | 65 | private static final long serialVersionUID = 1L; 66 | 67 | @Override 68 | protected void populateItem(ListItem item) { 69 | PivotFieldAction action = item.getModelObject(); 70 | AbstractLink link = action.getLink("link"); 71 | // link.add(new Image("image", action.getImage())); 72 | // link.add(AttributeModifier.replace("title", action.getTooltip())); 73 | link.add(new Label("name", action.getName())); 74 | item.add(link); 75 | } 76 | 77 | }; 78 | dropDownContainer.add(actionsView); 79 | } 80 | 81 | @Override 82 | protected void onInitialize() { 83 | super.onInitialize(); 84 | 85 | setVisible(!actionsModel.getObject().isEmpty()); 86 | } 87 | 88 | @Override 89 | public void renderHead(IHeaderResponse response) { 90 | super.renderHead(response); 91 | 92 | response.render(JavaScriptHeaderItem.forReference(new JQueryPluginResourceReference(PivotAreaPanel.class, "res/jquery.dropdown.js"))); 93 | response.render(CssHeaderItem.forReference(new PackageResourceReference(PivotAreaPanel.class, "res/jquery.dropdown.css"))); 94 | } 95 | 96 | private PivotField getPivotField() { 97 | return getModelObject(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ro.fortsoft.wicket.pivot 6 | wicket-pivot-parent 7 | 0.6.0-SNAPHOT 8 | 9 | 10 | 4.0.0 11 | wicket-pivot-demo 12 | 0.6.0-SNAPHOT 13 | war 14 | Wicket Pivot Demo 15 | 16 | 17 | 8.1.16.v20140903 18 | 1.6.4 19 | 1.2.16 20 | 21 | 22 | 23 | 24 | 25 | org.mortbay.jetty 26 | jetty-maven-plugin 27 | ${jetty.version} 28 | 29 | 30 | 31 | 8081 32 | 3600000 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-war-plugin 41 | 2.1.1 42 | 43 | root 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | ro.fortsoft.wicket.pivot 53 | wicket-pivot 54 | ${project.version} 55 | 56 | 57 | ro.fortsoft.wicket.pivot 58 | wicket-pivot-exporter 59 | ${project.version} 60 | 61 | 62 | 63 | 64 | org.apache.derby 65 | derby 66 | 10.9.1.0 67 | 68 | 69 | 70 | 71 | log4j 72 | log4j 73 | ${log4j.version} 74 | 75 | 76 | 77 | org.slf4j 78 | slf4j-api 79 | ${slf4j.version} 80 | 81 | 82 | 83 | org.slf4j 84 | slf4j-log4j12 85 | ${slf4j.version} 86 | 87 | 88 | 89 | com.google.code.gson 90 | gson 91 | 2.8.9 92 | 93 | 94 | 95 | 96 | junit 97 | junit 98 | 4.13.1 99 | test 100 | 101 | 102 | 103 | 104 | org.eclipse.jetty.aggregate 105 | jetty-all-server 106 | ${jetty.version} 107 | test 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/res/jquery.dropdown.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery dropdown: A simple dropdown plugin 3 | * 4 | * Inspired by Bootstrap: http://twitter.github.com/bootstrap/javascript.html#dropdowns 5 | * 6 | * Copyright 2013 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) 7 | * 8 | * Dual licensed under the MIT / GPL Version 2 licenses 9 | * 10 | */ 11 | if (jQuery) (function ($) { 12 | 13 | $.extend($.fn, { 14 | dropdown: function (method, data) { 15 | 16 | switch (method) { 17 | case 'show': 18 | show(null, $(this)); 19 | return $(this); 20 | case 'hide': 21 | hide(); 22 | return $(this); 23 | case 'attach': 24 | return $(this).attr('data-dropdown', data); 25 | case 'detach': 26 | hide(); 27 | return $(this).removeAttr('data-dropdown'); 28 | case 'disable': 29 | return $(this).addClass('dropdown-disabled'); 30 | case 'enable': 31 | hide(); 32 | return $(this).removeClass('dropdown-disabled'); 33 | } 34 | 35 | } 36 | }); 37 | 38 | function show(event, object) { 39 | 40 | var trigger = event ? $(this) : object, 41 | dropdown = $(trigger.attr('data-dropdown')), 42 | isOpen = trigger.hasClass('dropdown-open'); 43 | 44 | // In some cases we don't want to show it 45 | if (event) { 46 | if ($(event.target).hasClass('dropdown-ignore')) return; 47 | 48 | event.preventDefault(); 49 | event.stopPropagation(); 50 | } else { 51 | if (trigger !== object.target && $(object.target).hasClass('dropdown-ignore')) return; 52 | } 53 | hide(); 54 | 55 | if (isOpen || trigger.hasClass('dropdown-disabled')) return; 56 | 57 | // Show it 58 | trigger.addClass('dropdown-open'); 59 | dropdown 60 | .data('dropdown-trigger', trigger) 61 | .show(); 62 | 63 | // Position it 64 | position(); 65 | 66 | // Trigger the show callback 67 | dropdown 68 | .trigger('show', { 69 | dropdown: dropdown, 70 | trigger: trigger 71 | }); 72 | 73 | } 74 | 75 | function hide(event) { 76 | 77 | // In some cases we don't hide them 78 | var targetGroup = event ? $(event.target).parents().addBack() : null; 79 | 80 | // Are we clicking anywhere in a dropdown? 81 | if (targetGroup && targetGroup.is('.jquery-dropdown')) { 82 | // Is it a dropdown menu? 83 | if (targetGroup.is('.jquery-dropdown-menu')) { 84 | // Did we click on an option? If so close it. 85 | if (!targetGroup.is('A')) return; 86 | } else { 87 | // Nope, it's a panel. Leave it open. 88 | return; 89 | } 90 | } 91 | 92 | // Hide any dropdown that may be showing 93 | $(document).find('.jquery-dropdown:visible').each(function () { 94 | var dropdown = $(this); 95 | dropdown 96 | .hide() 97 | .removeData('dropdown-trigger') 98 | .trigger('hide', { dropdown: dropdown }); 99 | }); 100 | 101 | // Remove all dropdown-open classes 102 | $(document).find('.dropdown-open').removeClass('dropdown-open'); 103 | 104 | } 105 | 106 | function position() { 107 | 108 | var dropdown = $('.jquery-dropdown:visible').eq(0), 109 | trigger = dropdown.data('dropdown-trigger'), 110 | hOffset = trigger ? parseInt(trigger.attr('data-horizontal-offset') || 0, 10) : null, 111 | vOffset = trigger ? parseInt(trigger.attr('data-vertical-offset') || 0, 10) : null; 112 | 113 | if (dropdown.length === 0 || !trigger) return; 114 | 115 | // Position the dropdown relative-to-parent... 116 | if (dropdown.hasClass('dropdown-relative')) { 117 | dropdown.css({ 118 | left: dropdown.hasClass('dropdown-anchor-right') ? 119 | trigger.position().left - (dropdown.outerWidth(true) - trigger.outerWidth(true)) - parseInt(trigger.css('margin-right')) + hOffset : 120 | trigger.position().left + parseInt(trigger.css('margin-left')) + hOffset, 121 | top: trigger.position().top + trigger.outerHeight(true) - parseInt(trigger.css('margin-top')) + vOffset 122 | }); 123 | } else { 124 | // ...or relative to document 125 | dropdown.css({ 126 | left: dropdown.hasClass('dropdown-anchor-right') ? 127 | trigger.offset().left - (dropdown.outerWidth() - trigger.outerWidth()) + hOffset : trigger.offset().left + hOffset, 128 | top: trigger.offset().top + trigger.outerHeight() + vOffset 129 | }); 130 | } 131 | } 132 | 133 | $(document).on('click.dropdown', '[data-dropdown]', show); 134 | $(document).on('click.dropdown', hide); 135 | $(window).on('resize', position); 136 | 137 | })(jQuery); 138 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/PivotField.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | import org.apache.wicket.util.convert.IConverter; 20 | 21 | /** 22 | * @author Decebal Suiu 23 | */ 24 | public class PivotField implements Serializable, Comparable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | public static final int SORT_ORDER_UNSORTED = 0; 29 | public static final int SORT_ORDER_ASCENDING = 1; 30 | public static final int SORT_ORDER_DESCENDING = 2; 31 | 32 | private String name; 33 | private String title; 34 | private Area area; 35 | private int index; 36 | private int areaIndex; 37 | private Class type; 38 | private Aggregator aggregator; 39 | private FieldCalculation fieldCalculation; 40 | private IConverter converter; 41 | private int sortOrder; 42 | 43 | public PivotField(String name, int index) { 44 | this.name = name; 45 | this.index = index; 46 | 47 | sortOrder = SORT_ORDER_ASCENDING; 48 | aggregator = Aggregator.get(Aggregator.SUM); 49 | } 50 | 51 | public String getName() { 52 | return name; 53 | } 54 | 55 | public int getIndex() { 56 | return index; 57 | } 58 | 59 | public String getTitle() { 60 | return title; 61 | } 62 | 63 | public PivotField setTitle(String title) { 64 | this.title = title; 65 | return this; 66 | } 67 | 68 | public Area getArea() { 69 | return area; 70 | } 71 | 72 | public PivotField setArea(Area area) { 73 | this.area = area; 74 | return this; 75 | } 76 | 77 | public int getAreaIndex() { 78 | return areaIndex; 79 | } 80 | 81 | public PivotField setAreaIndex(int areaIndex) { 82 | this.areaIndex = areaIndex; 83 | return this; 84 | } 85 | 86 | public Class getType() { 87 | return type; 88 | } 89 | 90 | public PivotField setType(Class valueType) { 91 | this.type = valueType; 92 | return this; 93 | } 94 | 95 | public String getCalculationDescription() { 96 | if( getFieldCalculation() != null) 97 | return getFieldCalculation().getDescription(); 98 | return getAggregator().getFunction().toUpperCase(); 99 | } 100 | 101 | public Aggregator getAggregator() { 102 | return aggregator; 103 | } 104 | 105 | public void setAggregator(Aggregator aggregator) { 106 | this.aggregator = aggregator; 107 | } 108 | 109 | public boolean isNumber() { 110 | return Number.class.isAssignableFrom(type); 111 | } 112 | 113 | public IConverter getConverter() { 114 | return converter; 115 | } 116 | 117 | public void setConverter(IConverter converter) { 118 | this.converter = converter; 119 | } 120 | 121 | public int getSortOrder() { 122 | return sortOrder; 123 | } 124 | 125 | public void setSortOrder(int sortOrder) { 126 | this.sortOrder = sortOrder; 127 | } 128 | 129 | @Override 130 | public int compareTo(PivotField o) { 131 | return areaIndex - o.areaIndex; 132 | } 133 | 134 | @Override 135 | public String toString() { 136 | return "PivotField [name=" + name + ", title=" + title + ", area=" 137 | + area + ", index=" + index + ", areaIndex=" + areaIndex + "]"; 138 | } 139 | 140 | /** 141 | *Area of pivot field, which indicates whether the field is a column field, row field, or data field. 142 | */ 143 | public enum Area { 144 | 145 | ROW("row"), 146 | COLUMN("column"), 147 | DATA("data"), 148 | UNUSED("unused"); 149 | 150 | private String name; 151 | 152 | private Area(String name) { 153 | this.name = name; 154 | } 155 | 156 | public String getName() { 157 | return name; 158 | } 159 | 160 | public static List getValues() { 161 | List values = new ArrayList<>(); 162 | values.add(ROW); 163 | values.add(COLUMN); 164 | values.add(DATA); 165 | values.add(UNUSED); 166 | 167 | return values; 168 | } 169 | 170 | public static Area getValue(String name) { 171 | for (Area area : getValues()) { 172 | if (area.name.equals(name)) { 173 | return area; 174 | } 175 | } 176 | 177 | return null; 178 | } 179 | 180 | } 181 | 182 | /** 183 | * @internal Reset the calculation state 184 | */ 185 | void resetCalculation() { 186 | if (getAggregator() != null) 187 | getAggregator().init(); 188 | if (getFieldCalculation() != null) 189 | getFieldCalculation().init(); 190 | } 191 | 192 | public FieldCalculation getFieldCalculation() { 193 | return fieldCalculation; 194 | } 195 | 196 | public void setFieldCalculation(FieldCalculation fieldCalculation) { 197 | this.fieldCalculation = fieldCalculation; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/FieldCalculationPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import org.apache.wicket.ajax.AjaxRequestTarget; 16 | import org.apache.wicket.ajax.form.OnChangeAjaxBehavior; 17 | import org.apache.wicket.ajax.markup.html.AjaxLink; 18 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 19 | import org.apache.wicket.markup.html.form.ChoiceRenderer; 20 | import org.apache.wicket.markup.html.form.DropDownChoice; 21 | import org.apache.wicket.markup.html.form.TextField; 22 | import org.apache.wicket.markup.html.panel.GenericPanel; 23 | import org.apache.wicket.model.IModel; 24 | import org.apache.wicket.model.PropertyModel; 25 | import ro.fortsoft.wicket.pivot.FieldCalculation; 26 | import ro.fortsoft.wicket.pivot.PivotField; 27 | import ro.fortsoft.wicket.pivot.PivotModel; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | public class FieldCalculationPanel extends GenericPanel { 33 | private final static class OnChangeAjaxBehaviorExtension extends OnChangeAjaxBehavior { 34 | private static final long serialVersionUID = 1L; 35 | 36 | @Override 37 | protected void onUpdate(AjaxRequestTarget target) { 38 | // We just want the field content to be pushed to the server 39 | } 40 | } 41 | 42 | private static final class PivotFieldChoiceRenderer extends ChoiceRenderer { 43 | private static final long serialVersionUID = 1L; 44 | 45 | @Override 46 | public Object getDisplayValue(PivotField object) { 47 | return object.getTitle() + " (" + object.getCalculationDescription() + ")"; 48 | } 49 | } 50 | 51 | private static final long serialVersionUID = 1L; 52 | 53 | private FieldCalculation fieldCalculation; 54 | private PivotField fieldA; 55 | private PivotField fieldB; 56 | private String title = "Calculation"; 57 | private boolean okPressed; 58 | 59 | public FieldCalculationPanel(String id, IModel model, IModel pivotModel) { 60 | super(id, model); 61 | 62 | okPressed = false; 63 | 64 | setFieldCalculation(getModelObject().getFieldCalculation()); 65 | if (fieldCalculation != null) { 66 | fieldA = fieldCalculation.getFieldA(); 67 | fieldB = fieldCalculation.getFieldB(); 68 | } 69 | List fieldCalculations = new ArrayList<>(); 70 | for (String name : FieldCalculation.FUNCTIONS) 71 | fieldCalculations.add(FieldCalculation.get(name)); 72 | 73 | final DropDownChoice fieldCalculationDropDown = new DropDownChoice<>( 74 | "fieldCalcluation", new PropertyModel<>(this, "fieldCalculation"), fieldCalculations, 75 | new ChoiceRenderer("function") { 76 | private static final long serialVersionUID = 1L; 77 | 78 | @Override 79 | public Object getDisplayValue(FieldCalculation object) { 80 | return object == null ? "" : object.getDescription(); 81 | } 82 | }); 83 | fieldCalculationDropDown.setOutputMarkupId(true); 84 | fieldCalculationDropDown.add(new OnChangeAjaxBehaviorExtension()); 85 | add(fieldCalculationDropDown); 86 | 87 | List fields = new ArrayList<>(); 88 | for (PivotField field : pivotModel.getObject().getFields()) { 89 | if (field.getAggregator() != null && field.getFieldCalculation() == null && field.isNumber()) 90 | fields.add(field); 91 | } 92 | final DropDownChoice fieldADropDown = new DropDownChoice<>("fieldA", 93 | new PropertyModel<>(this, "fieldA"), fields, new PivotFieldChoiceRenderer()); 94 | fieldADropDown.setOutputMarkupId(true); 95 | fieldADropDown.add(new OnChangeAjaxBehaviorExtension()); 96 | add(fieldADropDown); 97 | 98 | final DropDownChoice fieldBDropDown = new DropDownChoice<>("fieldB", 99 | new PropertyModel<>(this, "fieldB"), fields, new PivotFieldChoiceRenderer()); 100 | fieldBDropDown.setOutputMarkupId(true); 101 | fieldBDropDown.add(new OnChangeAjaxBehaviorExtension()); 102 | add(fieldBDropDown); 103 | 104 | add(new TextField<>("title", new PropertyModel<>(this, "title")) 105 | .add(new OnChangeAjaxBehaviorExtension())); 106 | 107 | add(new AjaxLink("ok") { 108 | private static final long serialVersionUID = 1L; 109 | 110 | @Override 111 | public void onClick(AjaxRequestTarget target) { 112 | if (fieldCalculation != null) { 113 | okPressed = true; 114 | fieldCalculation.setFieldA(fieldA); 115 | fieldCalculation.setFieldB(fieldB); 116 | } 117 | ModalWindow.closeCurrent(target); 118 | } 119 | }); 120 | } 121 | 122 | public boolean isOkPressed() { 123 | return okPressed; 124 | } 125 | 126 | public FieldCalculation getFieldCalculation() { 127 | return fieldCalculation; 128 | } 129 | 130 | public void setFieldCalculation(FieldCalculation fieldCalculation) { 131 | this.fieldCalculation = fieldCalculation; 132 | } 133 | 134 | public PivotField getFieldA() { 135 | return fieldA; 136 | } 137 | 138 | public void setFieldA(PivotField fieldA) { 139 | this.fieldA = fieldA; 140 | } 141 | 142 | public PivotField getFieldB() { 143 | return fieldB; 144 | } 145 | 146 | public void setFieldB(PivotField fieldB) { 147 | this.fieldB = fieldB; 148 | } 149 | 150 | public String getTitle() { 151 | return title; 152 | } 153 | 154 | public void setTitle(String title) { 155 | this.title = title; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | org.sonatype.oss 6 | oss-parent 7 | 7 8 | 9 | 10 | 4.0.0 11 | ro.fortsoft.wicket.pivot 12 | wicket-pivot-parent 13 | 0.6.0-SNAPHOT 14 | pom 15 | Wicket Pivot Parent 16 | Simple web pivot table using Apache Wicket 17 | 18 | 19 | 20 | The Apache Software License, Version 2.0 21 | http://www.apache.org/licenses/LICENSE-2.0.txt 22 | repo 23 | 24 | 25 | 26 | 27 | scm:git:https://github.com/decebals/wicket-pivot.git 28 | scm:git:https://github.com/decebals/wicket-pivot.git 29 | git@github.com/decebals/wicket-pivot.git 30 | HEAD 31 | 32 | 33 | 34 | UTF-8 35 | 8.12.0 36 | 37 | 38 | 39 | 40 | 41 | false 42 | src/main/java 43 | 44 | **/*.java 45 | 46 | 47 | 48 | src/main/resources 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 3.8.0 57 | 58 | 1.8 59 | 1.8 60 | true 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-javadoc-plugin 67 | 68 | 69 | 70 | jar 71 | 72 | 73 | 74 | 75 | 76 | 77 | org.apache.maven.plugins 78 | maven-source-plugin 79 | 80 | 81 | 82 | jar 83 | 84 | 85 | 86 | 87 | 88 | 89 | org.apache.maven.plugins 90 | maven-release-plugin 91 | 2.3.2 92 | 93 | deploy 94 | true 95 | release-@{project.version} 96 | 97 | 98 | 99 | 100 | maven-resources-plugin 101 | 2.4.3 102 | 103 | 104 | 105 | maven-jar-plugin 106 | 3.1.2 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | org.apache.wicket 115 | wicket-core 116 | ${wicket.version} 117 | 118 | 119 | 120 | org.apache.wicket 121 | wicket-extensions 122 | ${wicket.version} 123 | 124 | 125 | 126 | 127 | wicket-pivot 128 | wicket-pivot-exporter 129 | demo 130 | 131 | 132 | 133 | 134 | release-sign-artifacts 135 | 136 | 137 | performRelease 138 | true 139 | 140 | 141 | 142 | 143 | 144 | org.apache.maven.plugins 145 | maven-gpg-plugin 146 | 147 | 148 | sign-artifacts 149 | verify 150 | 151 | sign 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/FieldCalculation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | import java.util.Arrays; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | public abstract class FieldCalculation implements Serializable { 21 | private static final long serialVersionUID = 1L; 22 | public static final String PERCENT_OF = "percentOf"; 23 | public static final String SUBTRACT = "subtract"; 24 | public static final String ADDITION = "addition"; 25 | 26 | public static final List FUNCTIONS = Collections.unmodifiableList(Arrays.asList(PERCENT_OF, SUBTRACT, 27 | ADDITION)); 28 | private PivotField fieldA; 29 | private PivotField fieldB; 30 | 31 | public static List getFunctions() { 32 | return FUNCTIONS; 33 | } 34 | 35 | public static FieldCalculation get(String function) { 36 | if (function.equalsIgnoreCase(PERCENT_OF)) { 37 | return new PercentOf(); 38 | } else if (function.equalsIgnoreCase(SUBTRACT)) { 39 | return new Substract(); 40 | } else if (function.equalsIgnoreCase(ADDITION)) { 41 | return new Addition(); 42 | } 43 | return null; 44 | } 45 | 46 | /** 47 | * Provide 48 | */ 49 | @FunctionalInterface 50 | public interface FieldValueProvider { 51 | /** 52 | * @param field 53 | * can be null 54 | * 55 | * @return the current value of the pivot field 56 | */ 57 | Object getFieldValue(PivotField field); 58 | } 59 | 60 | public abstract FieldCalculation init(); 61 | 62 | public abstract String getFunction(); 63 | 64 | public abstract String getDescription(); 65 | 66 | public abstract Object calculate(FieldValueProvider fieldValueProvider); 67 | 68 | protected String getFieldDescription(PivotField field) { 69 | if (field == null) 70 | return ""; 71 | return field.getTitle() + " (" + field.getCalculationDescription() + ")"; 72 | } 73 | 74 | public static class PercentOf extends FieldCalculation { 75 | private static final long serialVersionUID = 1L; 76 | 77 | @Override 78 | public FieldCalculation init() { 79 | return this; 80 | } 81 | 82 | @Override 83 | public String getFunction() { 84 | return PERCENT_OF; 85 | } 86 | 87 | @Override 88 | public Object calculate(FieldValueProvider fieldValueProvider) { 89 | Object valueA = fieldValueProvider.getFieldValue(getFieldA()); 90 | Object valueB = fieldValueProvider.getFieldValue(getFieldB()); 91 | if (valueA instanceof Number && valueB instanceof Number) { 92 | Number numberA = (Number) valueA; 93 | Number numberB = (Number) valueB; 94 | if (Math.abs(numberB.doubleValue()) > 0.000001) 95 | return 100.0 / numberB.doubleValue() * numberA.doubleValue(); 96 | } 97 | return 0; 98 | } 99 | 100 | @Override 101 | public String getDescription() { 102 | return "% " + getFieldDescription(getFieldA()) + " of " + getFieldDescription(getFieldB()); 103 | } 104 | } 105 | 106 | public static class Substract extends FieldCalculation { 107 | private static final long serialVersionUID = 1L; 108 | 109 | @Override 110 | public FieldCalculation init() { 111 | return this; 112 | } 113 | 114 | @Override 115 | public String getFunction() { 116 | return SUBTRACT; 117 | } 118 | 119 | @Override 120 | public Object calculate(FieldValueProvider fieldValueProvider) { 121 | Object valueA = fieldValueProvider.getFieldValue(getFieldA()); 122 | Object valueB = fieldValueProvider.getFieldValue(getFieldB()); 123 | if (valueA instanceof Number && valueB instanceof Number) { 124 | Number numberA = (Number) valueA; 125 | Number numberB = (Number) valueB; 126 | return numberA.doubleValue() - numberB.doubleValue(); 127 | } 128 | return 0; 129 | } 130 | 131 | @Override 132 | public String getDescription() { 133 | return getFieldDescription(getFieldA()) + " - " + getFieldDescription(getFieldB()); 134 | } 135 | } 136 | 137 | public static class Addition extends FieldCalculation { 138 | private static final long serialVersionUID = 1L; 139 | 140 | @Override 141 | public FieldCalculation init() { 142 | return this; 143 | } 144 | 145 | @Override 146 | public String getFunction() { 147 | return ADDITION; 148 | } 149 | 150 | @Override 151 | public Object calculate(FieldValueProvider fieldValueProvider) { 152 | Object valueA = fieldValueProvider.getFieldValue(getFieldA()); 153 | Object valueB = fieldValueProvider.getFieldValue(getFieldB()); 154 | if (valueA instanceof Number && valueB instanceof Number) { 155 | Number numberA = (Number) valueA; 156 | Number numberB = (Number) valueB; 157 | return numberA.doubleValue() + numberB.doubleValue(); 158 | } 159 | return 0; 160 | } 161 | 162 | @Override 163 | public String getDescription() { 164 | return getFieldDescription(getFieldA()) + " + " + getFieldDescription(getFieldB()); 165 | } 166 | } 167 | 168 | @Override 169 | public boolean equals(Object obj) { 170 | if (obj instanceof FieldCalculation) { 171 | String tmp = ((FieldCalculation) obj).getFunction(); 172 | return getFunction().equals(tmp); 173 | } 174 | 175 | return false; 176 | } 177 | 178 | @Override 179 | public int hashCode() { 180 | return getFunction().hashCode(); 181 | } 182 | 183 | public PivotField getFieldA() { 184 | return fieldA; 185 | } 186 | 187 | public void setFieldA(PivotField fieldA) { 188 | this.fieldA = fieldA; 189 | } 190 | 191 | public PivotField getFieldB() { 192 | return fieldB; 193 | } 194 | 195 | public void setFieldB(PivotField fieldB) { 196 | this.fieldB = fieldB; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import org.apache.wicket.Application; 16 | import org.apache.wicket.MetaDataKey; 17 | import org.apache.wicket.request.resource.PackageResourceReference; 18 | import org.apache.wicket.request.resource.ResourceReference; 19 | 20 | /** 21 | * @author Decebal Suiu 22 | */ 23 | public class PivotSettings { 24 | 25 | @SuppressWarnings("serial") 26 | private static final MetaDataKey KEY = new MetaDataKey() {}; 27 | 28 | private ResourceReference javaScriptReference = new PackageResourceReference( 29 | PivotSettings.class, "res/pivot.js"); 30 | private ResourceReference cssReference = new PackageResourceReference( 31 | PivotSettings.class, "res/pivot.css"); 32 | private ResourceReference jqueryReference = new PackageResourceReference( 33 | PivotSettings.class, "res/jquery-1.8.3.min.js"); 34 | private ResourceReference jqueryUIReference = new PackageResourceReference( 35 | PivotSettings.class, "res/jquery-ui-1.9.2.min.js"); 36 | private ResourceReference jqueryJsonReference = new PackageResourceReference( 37 | PivotSettings.class, "res/jquery.json-2.2.min.js"); 38 | private ResourceReference bootstrapJavaScriptReference = new PackageResourceReference( 39 | PivotSettings.class, "res/bootstrap.js"); 40 | private ResourceReference bootstrapCssReference = new PackageResourceReference( 41 | PivotSettings.class, "res/bootstrap.css"); 42 | 43 | private boolean includeJQuery = false; 44 | private boolean includeJQueryUI = true; 45 | private boolean includeJQueryJson = true; 46 | private boolean includeJavaScript = true; 47 | private boolean includeCss = true; 48 | private boolean includeBootstrap = true; 49 | 50 | /** 51 | * Private constructor, use {@link #get()} instead. 52 | */ 53 | private PivotSettings() { 54 | } 55 | 56 | public boolean isIncludeJQuery() { 57 | return includeJQuery; 58 | } 59 | 60 | public PivotSettings setIncludeJQuery(boolean includeJQuery) { 61 | this.includeJQuery = includeJQuery; 62 | return this; 63 | } 64 | 65 | public boolean isIncludeJQueryUI() { 66 | return includeJQueryUI; 67 | } 68 | 69 | public PivotSettings setIncludeJQueryUI(boolean includeJQueryUI) { 70 | this.includeJQueryUI = includeJQueryUI; 71 | return this; 72 | } 73 | 74 | public boolean isIncludeJQueryJson() { 75 | return includeJQueryJson; 76 | } 77 | 78 | public void setIncludeJQueryJson(boolean includeJQueryJson) { 79 | this.includeJQueryJson = includeJQueryJson; 80 | } 81 | 82 | public boolean isIncludeJavaScript() { 83 | return includeJavaScript; 84 | } 85 | 86 | public PivotSettings setIncludeJavascript(boolean includeJavaScript) { 87 | this.includeJavaScript = includeJavaScript; 88 | return this; 89 | } 90 | 91 | public boolean isIncludeCss() { 92 | return includeCss; 93 | } 94 | 95 | public PivotSettings setIncludeCss(boolean includeCss) { 96 | this.includeCss = includeCss; 97 | return this; 98 | } 99 | 100 | public boolean isIncludeBootstrap() { 101 | return includeBootstrap; 102 | } 103 | 104 | public PivotSettings setIncludeBootstrap(boolean includeBootstrap) { 105 | this.includeBootstrap = includeBootstrap; 106 | return this; 107 | } 108 | 109 | public ResourceReference getJQueryReference() { 110 | return jqueryReference; 111 | } 112 | 113 | public PivotSettings setJQueryReference(ResourceReference jqueryReference) { 114 | this.jqueryReference = jqueryReference; 115 | return this; 116 | } 117 | 118 | public ResourceReference getJQueryUIReference() { 119 | return jqueryUIReference; 120 | } 121 | 122 | public PivotSettings setJQueryUIReference(ResourceReference jqueryUIReference) { 123 | this.jqueryUIReference = jqueryUIReference; 124 | return this; 125 | } 126 | 127 | public ResourceReference getJQueryJsonReference() { 128 | return jqueryJsonReference; 129 | } 130 | 131 | public PivotSettings setJQueryJsonReference(ResourceReference jqueryJsonReference) { 132 | this.jqueryJsonReference = jqueryJsonReference; 133 | return this; 134 | } 135 | 136 | public ResourceReference getJavaScriptReference() { 137 | return javaScriptReference; 138 | } 139 | 140 | public PivotSettings setJavaScriptReference(ResourceReference javaScriptReference) { 141 | this.javaScriptReference = javaScriptReference; 142 | return this; 143 | } 144 | 145 | public ResourceReference getCssReference() { 146 | return cssReference; 147 | } 148 | 149 | public PivotSettings setCssReference(ResourceReference cssReference) { 150 | this.cssReference = cssReference; 151 | return this; 152 | } 153 | 154 | public ResourceReference getBootstrapJavaScriptReference() { 155 | return bootstrapJavaScriptReference; 156 | } 157 | 158 | public PivotSettings setBootstrapJavaScriptReference(ResourceReference bootstrapJavaScriptReference) { 159 | this.bootstrapJavaScriptReference = bootstrapJavaScriptReference; 160 | return this; 161 | } 162 | 163 | public ResourceReference getBootstrapCssReference() { 164 | return bootstrapCssReference; 165 | } 166 | 167 | public PivotSettings setBootstrapCssReference(ResourceReference bootstrapCssReference) { 168 | this.bootstrapCssReference = bootstrapCssReference; 169 | return this; 170 | } 171 | 172 | /** 173 | * Retrieves the instance of settings object. 174 | * 175 | * @return settings instance 176 | */ 177 | public static PivotSettings get() { 178 | Application application = Application.get(); 179 | PivotSettings settings = application.getMetaData(KEY); 180 | if (settings == null) { 181 | synchronized (application) { 182 | settings = application.getMetaData(KEY); 183 | if (settings == null) { 184 | settings = new PivotSettings(); 185 | application.setMetaData(KEY, settings); 186 | } 187 | } 188 | } 189 | 190 | return application.getMetaData(KEY); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simple wicket pivot table 2 | ===================== 3 | A pivot table is a data summarization tool found in data visualization programs such as spreadsheets or business intelligence software. 4 | For more information what it's a pivot table see http://en.wikipedia.org/wiki/Pivot_table. 5 | 6 | Current build status: [![Build Status](https://buildhive.cloudbees.com/job/decebals/job/wicket-pivot/badge/icon)](https://buildhive.cloudbees.com/job/decebals/job/wicket-pivot/) 7 | 8 | ![Image of Demo](wicket-pivot.png) 9 | 10 | Components 11 | ------------------- 12 | - **PivotDataSource** is the data source for pivot table (pivot's fields). The data can be fetched from a sql ResultSet (see ResultSetPivotDataSource) 13 | or other non sql sources. 14 | - **PivotModel** is the place where I put the pivot configuration, here I can specify what fields are on each area (ROW, COLUMN, DATA) 15 | and what is their ordering. Also, here I can mention if I want a grand total on rows and/or columns. 16 | - **PivotTable** is the component that displays the pivot and it takes a PivotModel object as parameter. 17 | - **PivotField** is the object that can be put on a pivot's area. This object has a name and an index. 18 | As a constraint, on pivot's data area must be minimum one field and minimum one field on row or column areas. 19 | Also on aria DATA you can put only fields with Number type. 20 | The pivot fields allow several types of aggregations including sum, average, min, max, count. 21 | - **PivotExporter** is the interface for the pivot exporter plugins (builtin support for Csv and Xls) 22 | 23 | Artifacts 24 | ------------------- 25 | - Wicket Pivot `wicket-pivot` (jar) 26 | - Wicket Pivot Demo `wicket-pivot-demo` (war) 27 | 28 | Using Maven 29 | ------------------- 30 | In your pom.xml you must define the dependencies to Wicket Pivot artifacts with: 31 | 32 | ```xml 33 | 34 | ro.fortsoft.wicket.pivot 35 | wicket-pivot 36 | ${wicket-pivot.version} 37 | 38 | ``` 39 | 40 | where ${wicket-pivot.version} is the last wicket-pivot version. 41 | 42 | You may want to check for the latest released version using [Maven Search](http://search.maven.org/#search%7Cga%7C1%7Cwicket-pivot) 43 | 44 | How to use 45 | ------------------- 46 | It's very simple to add a pivot table in your wicket application. 47 | 48 | ```java 49 | PivotDataSource pivotDataSource = ...; 50 | add(new PivotPanel("pivot", pivotDataSource)); 51 | ``` 52 | 53 | First of all you must create a PivotDataSource and secondly add the pivot panel in your page. 54 | 55 | If you want to add programmatically some fields on the pivot areas 56 | 57 | ```java 58 | pivotModel.getField("REGION").setArea(PivotField.Area.ROW); 59 | pivotModel.getField("SALESMAN").setArea(PivotField.Area.ROW).setAreaIndex(1); 60 | pivotModel.getField("YEAR").setArea(PivotField.Area.COLUMN); 61 | pivotModel.getField("MONTH").setArea(PivotField.Area.COLUMN).setAreaIndex(1); 62 | pivotModel.getField("SALES").setArea(PivotField.Area.DATA); 63 | ``` 64 | 65 | You can specify an aggregator for a data pivot field 66 | 67 | ```java 68 | pivotModel.getField("SALES").setAggregator(Aggregator.get(Aggregator.COUNT)); 69 | ``` 70 | 71 | or 72 | 73 | ```java 74 | pivotModel.getField("MONEY").setAggregator(new Aggregator.Count()); 75 | ``` 76 | 77 | You can specify a custom converter (wicket `IConverter`) for a field 78 | 79 | ```java 80 | pivotModel.getField("SALES").setConverter(new DoubleConverter() { 81 | 82 | @Override 83 | public NumberFormat getNumberFormat(Locale locale) { 84 | NumberFormat format = super.getNumberFormat(locale); 85 | format.setMinimumFractionDigits(2); 86 | 87 | return format; 88 | } 89 | 90 | }); 91 | ``` 92 | 93 | If you don't set a converter for a field then this field (the values) will be rendered 94 | with a converter supplied by wicket according to the field type. 95 | 96 | If you want to display programmatically a grand total on rows and/or columns 97 | 98 | ```java 99 | pivotModel.setShowGrandTotalForColumn(true); 100 | pivotModel.setShowGrandTotalForRow(true); 101 | ``` 102 | 103 | Also, Wicket Pivot has support for Field calculations, e.g: 104 | 105 | - % fieldA of fieldB (PercentOf); when you need to display how much % are the costs from the revenue. I.e. how much % of our revenue do we give to Google 106 | - field A + fieldB (Addition) 107 | - fieldA - fieldB (Substract) 108 | 109 | Another nice feature is auto calculate (a check box component). When you check this feature than on each pivot modifications (area, aggregation, etc) you can see the result in real time. 110 | 111 | Internationalization 112 | ------------------- 113 | Wicket-pivot has support for internationalization. 114 | Supported languages: 115 | - English 116 | - German 117 | - Romanian 118 | 119 | If you want support for another languages please create and send a pull request (or an email) with the translation of [wicket-package.properties](https://github.com/decebals/wicket-pivot/blob/master/wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/wicket-package.properties). 120 | 121 | Demo 122 | ------------------- 123 | I have a tiny demo application with a demo apache derby embedded database. The demo application is in demo package. 124 | To run the demo application use: 125 | 126 | ```bash 127 | mvn install 128 | cd demo 129 | mvn jetty:run 130 | ``` 131 | 132 | In the internet browser type http://localhost:8081/. 133 | 134 | In demo pivot page put some fields on the areas (for example "REGION", "SALESMAN" on ROW area, "YEAR", "MONTH" 135 | on COLUMN area and "SALES" on DATA area) and press the "Show pivot" button. 136 | 137 | You can see a screenshot from demo application in [wiki page] (https://github.com/decebals/wicket-pivot/wiki). 138 | 139 | Versioning 140 | ------------ 141 | Wicket-pivot will be maintained under the Semantic Versioning guidelines as much as possible. 142 | 143 | Releases will be numbered with the follow format: 144 | 145 | `..` 146 | 147 | And constructed with the following guidelines: 148 | 149 | * Breaking backward compatibility bumps the major 150 | * New additions without breaking backward compatibility bumps the minor 151 | * Bug fixes and misc changes bump the patch 152 | 153 | For more information on SemVer, please visit http://semver.org/. 154 | 155 | License 156 | -------------- 157 | Copyright 2012 Decebal Suiu 158 | 159 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 160 | the License. You may obtain a copy of the License in the LICENSE file, or at: 161 | 162 | http://www.apache.org/licenses/LICENSE-2.0 163 | 164 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 165 | an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 166 | specific language governing permissions and limitations under the License. 167 | -------------------------------------------------------------------------------- /wicket-pivot-exporter/src/main/java/ro/fortsoft/wicket/pivot/exporter/PivotXlsExporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.exporter; 14 | 15 | import org.apache.poi.hssf.usermodel.HSSFCellStyle; 16 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; 17 | import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined; 18 | import org.apache.poi.ss.usermodel.*; 19 | import org.apache.poi.ss.util.CellRangeAddress; 20 | import ro.fortsoft.wicket.pivot.PivotModel; 21 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel; 22 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.*; 23 | 24 | import java.awt.*; 25 | import java.io.IOException; 26 | import java.io.OutputStream; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | 30 | /** 31 | * Basic XLS exporter 32 | */ 33 | public class PivotXlsExporter implements PivotExporter { 34 | private static final long serialVersionUID = 1L; 35 | 36 | private static class StyleContext { 37 | private CellStyle headerStyle; 38 | private CellStyle dataHeaderStyle; 39 | private HSSFCellStyle grandTotalStyle; 40 | 41 | StyleContext(HSSFWorkbook wb) { 42 | headerStyle = wb.createCellStyle(); 43 | 44 | headerStyle.setBorderTop(BorderStyle.MEDIUM); 45 | headerStyle.setBorderLeft(BorderStyle.MEDIUM); 46 | headerStyle.setBorderRight(BorderStyle.MEDIUM); 47 | headerStyle.setBorderBottom(BorderStyle.MEDIUM); 48 | headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); 49 | headerStyle.setFillForegroundColor(HSSFColorPredefined.GREY_25_PERCENT.getIndex()); 50 | 51 | dataHeaderStyle = wb.createCellStyle(); 52 | dataHeaderStyle.setBorderTop(BorderStyle.MEDIUM); 53 | dataHeaderStyle.setBorderLeft(BorderStyle.MEDIUM); 54 | dataHeaderStyle.setBorderRight(BorderStyle.MEDIUM); 55 | dataHeaderStyle.setBorderBottom(BorderStyle.MEDIUM); 56 | dataHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); 57 | dataHeaderStyle.setFillForegroundColor(HSSFColorPredefined.GREY_25_PERCENT.getIndex()); 58 | 59 | grandTotalStyle = wb.createCellStyle(); 60 | grandTotalStyle.setBorderTop(BorderStyle.MEDIUM); 61 | grandTotalStyle.setBorderLeft(BorderStyle.MEDIUM); 62 | grandTotalStyle.setBorderRight(BorderStyle.MEDIUM); 63 | grandTotalStyle.setBorderBottom(BorderStyle.MEDIUM); 64 | grandTotalStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); 65 | grandTotalStyle.setFillForegroundColor(HSSFColorPredefined.LIGHT_TURQUOISE.getIndex()); 66 | } 67 | } 68 | 69 | @Override 70 | public void exportPivot(PivotModel pivotModel, OutputStream outputStream) throws IOException { 71 | PivotTableRenderModel renderModel = PivotTableRenderModel.create(pivotModel); 72 | 73 | HSSFWorkbook wb = new HSSFWorkbook(); 74 | Sheet sheetData = wb.createSheet("Pivot"); 75 | 76 | Map rowSpanMap = new HashMap<>(); 77 | StyleContext styleContext = new StyleContext(wb); 78 | 79 | int rowNumber = 0; 80 | int maxColNum = 0; 81 | for (RenderRow row : renderModel.getAllRenderRows()) { 82 | int col = 0; 83 | Row poiRow = sheetData.createRow(rowNumber); 84 | for (RenderCell cell : row.getRenderCells()) { 85 | maxColNum = Math.max(maxColNum, col); 86 | 87 | /* 88 | * Check if we currently have a rowspan at this column from the 89 | * parent row. We only support a colspan of 1 at the moment 90 | * here, if rowspan > 1 91 | */ 92 | Integer rowSpan = rowSpanMap.get(col); 93 | if (rowSpan != null) { 94 | rowSpan--; 95 | if (rowSpan == 0) 96 | rowSpanMap.remove(col); 97 | else 98 | rowSpanMap.put(col, rowSpan); 99 | col++; 100 | } 101 | 102 | Cell poiCell = poiRow.createCell(col); 103 | 104 | /* 105 | * Output the Value 106 | */ 107 | Object rawValue = cell.getRawValue(); 108 | if (rawValue != null) { 109 | if (rawValue instanceof Double) { 110 | poiCell.setCellValue((Double) rawValue); 111 | } else { 112 | poiCell.setCellValue(String.valueOf(rawValue)); 113 | } 114 | } 115 | 116 | styleCell(poiCell, cell, styleContext); 117 | 118 | if (cell.getRowspan() > 1) { 119 | rowSpanMap.put(col, cell.getRowspan() - 1); 120 | sheetData.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber + cell.getRowspan() - 1, col, 121 | col)); 122 | } 123 | 124 | /* 125 | * We only support colspan _OR_ rowspan. The current PivotTable 126 | * also doesnt have rowspan and colspan at the same time. 127 | */ 128 | if (cell.getColspan() > 1) { 129 | sheetData.addMergedRegion(new CellRangeAddress(rowNumber, rowNumber, col, col + cell.getColspan() 130 | - 1)); 131 | } 132 | 133 | col++; 134 | for (int i = 1; i < cell.getColspan(); i++) { 135 | col++; 136 | } 137 | } 138 | rowNumber++; 139 | } 140 | 141 | autoSizeColumns(sheetData, maxColNum); 142 | 143 | wb.write(outputStream); 144 | outputStream.flush(); 145 | } 146 | 147 | private void styleCell(Cell poiCell, RenderCell cell, StyleContext styleContext) { 148 | if (cell instanceof HeaderRenderCell) 149 | poiCell.setCellStyle(styleContext.headerStyle); 150 | if (cell instanceof GrandTotalHeaderRenderCell) 151 | poiCell.setCellStyle(styleContext.grandTotalStyle); 152 | if( cell instanceof GrandTotalValueRenderCell) 153 | poiCell.setCellStyle(styleContext.grandTotalStyle); 154 | if (cell instanceof DataHeaderRenderCell) 155 | poiCell.setCellStyle(styleContext.dataHeaderStyle); 156 | } 157 | 158 | private void autoSizeColumns(Sheet sheetData, int maxColNum) { 159 | try { 160 | // Autosize columns 161 | int width = 0; 162 | for (int col = 0; col < maxColNum; col++) { 163 | sheetData.autoSizeColumn(col); 164 | int cwidth = sheetData.getColumnWidth(col); 165 | cwidth += 500; 166 | sheetData.setColumnWidth(col, cwidth); 167 | width += cwidth; 168 | } 169 | 170 | // calculate zoom factor 171 | int nominator = 45000 * 100 / width; 172 | if (nominator < 100) 173 | sheetData.setZoom(nominator); 174 | 175 | } catch (HeadlessException he) { 176 | // No UI, no autosize :( 177 | } 178 | } 179 | 180 | @Override 181 | public String getFormatName() { 182 | return "XLS"; 183 | } 184 | 185 | @Override 186 | public String getFormatMimetype() { 187 | return "application/vnd.ms-excel"; 188 | } 189 | 190 | @Override 191 | public String getFilenameExtension() { 192 | return "xls"; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotAreaPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import java.io.IOException; 16 | import java.util.HashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | import org.apache.wicket.AttributeModifier; 21 | import org.apache.wicket.Component; 22 | import org.apache.wicket.ajax.AjaxRequestTarget; 23 | import org.apache.wicket.event.Broadcast; 24 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 25 | import org.apache.wicket.markup.head.IHeaderResponse; 26 | import org.apache.wicket.markup.head.OnDomReadyHeaderItem; 27 | import org.apache.wicket.markup.html.WebMarkupContainer; 28 | import org.apache.wicket.markup.html.basic.Label; 29 | import org.apache.wicket.markup.html.list.ListItem; 30 | import org.apache.wicket.markup.html.list.ListView; 31 | import org.apache.wicket.markup.html.panel.EmptyPanel; 32 | import org.apache.wicket.markup.html.panel.Panel; 33 | import org.apache.wicket.model.AbstractReadOnlyModel; 34 | import org.apache.wicket.model.IModel; 35 | import org.apache.wicket.model.LoadableDetachableModel; 36 | import org.apache.wicket.model.Model; 37 | import org.apache.wicket.model.ResourceModel; 38 | import org.apache.wicket.util.template.PackageTextTemplate; 39 | 40 | import ro.fortsoft.wicket.pivot.PivotField; 41 | import ro.fortsoft.wicket.pivot.PivotModel; 42 | 43 | /** 44 | * @author Decebal Suiu 45 | */ 46 | public class PivotAreaPanel extends Panel { 47 | 48 | private static final long serialVersionUID = 1L; 49 | 50 | private SortableAjaxBehavior sortableAjaxBehavior; 51 | private ListView fieldsView; 52 | private PivotField.Area area; 53 | private ModalWindow modal; 54 | 55 | public PivotAreaPanel(String id, PivotField.Area area) { 56 | super(id); 57 | 58 | this.area = area; 59 | 60 | modal = new ModalWindow("modal"); 61 | modal.setAutoSize(true); 62 | add(modal); 63 | 64 | add(new Label("name", new ResourceModel(area.getName()))); 65 | 66 | WebMarkupContainer fieldsContainer = new WebMarkupContainer("fieldsContainer"); 67 | fieldsContainer.setOutputMarkupId(true); 68 | fieldsContainer.setMarkupId("area-" + area.getName() + "-" + getSession().nextSequenceValue()); 69 | add(fieldsContainer); 70 | 71 | fieldsView = new ListView("fields") { 72 | 73 | private static final long serialVersionUID = 1L; 74 | 75 | @Override 76 | protected void populateItem(ListItem item) { 77 | final IModel itemModel = item.getModel(); 78 | final PivotField pivotField = itemModel.getObject(); 79 | final PivotField.Area area = PivotAreaPanel.this.area; 80 | Label fieldLabel = new Label("field", new AbstractReadOnlyModel() { 81 | 82 | private static final long serialVersionUID = 1L; 83 | 84 | @Override 85 | public String getObject() { 86 | String title = pivotField.getTitle(); 87 | if (area.equals(PivotField.Area.DATA)) { 88 | title += " (" + pivotField.getCalculationDescription() + ")"; 89 | } 90 | 91 | return title; 92 | } 93 | 94 | }); 95 | if (pivotField.isNumber()) { 96 | item.add(AttributeModifier.append("class", "field-number")); 97 | } 98 | 99 | // add field actions panel 100 | if (!area.equals(PivotField.Area.UNUSED)) { 101 | PivotFieldActionsPanel pivotFieldActionsPanel = new PivotFieldActionsPanel("dropDownPanel", Model.of(pivotField), Model.of(getPivotModel())); 102 | pivotFieldActionsPanel.setRenderBodyOnly(true); 103 | item.add(pivotFieldActionsPanel); 104 | String markupId = "dropdown-" + pivotField.getIndex(); 105 | pivotFieldActionsPanel.get("dropdown").setMarkupId(markupId); 106 | fieldLabel.add(AttributeModifier.append("data-dropdown", "#" + markupId)); 107 | } else { 108 | item.add(new EmptyPanel("dropDownPanel").setVisible(false)); 109 | } 110 | 111 | item.add(fieldLabel); 112 | item.setOutputMarkupId(true); 113 | item.setMarkupId("field-" + pivotField.getIndex()); 114 | } 115 | }; 116 | fieldsView.setOutputMarkupPlaceholderTag(true); 117 | fieldsContainer.add(fieldsView); 118 | 119 | setOutputMarkupId(true); 120 | } 121 | 122 | @Override 123 | protected void onBeforeRender() { 124 | IModel> model = new LoadableDetachableModel>() { 125 | 126 | private static final long serialVersionUID = 1L; 127 | 128 | @Override 129 | protected List load() { 130 | return getPivotModel().getFields(area); 131 | } 132 | 133 | }; 134 | 135 | fieldsView.setModel(model); 136 | 137 | super.onBeforeRender(); 138 | } 139 | 140 | @Override 141 | protected void onInitialize() { 142 | super.onInitialize(); 143 | 144 | addSortableBehavior(get("fieldsContainer")); 145 | } 146 | 147 | public PivotField.Area getArea() { 148 | return area; 149 | } 150 | 151 | public ModalWindow getModal() { 152 | return modal; 153 | } 154 | 155 | @Override 156 | public void renderHead(IHeaderResponse response) { 157 | super.renderHead(response); 158 | 159 | CharSequence script = sortableAjaxBehavior.getCallbackFunctionBody(); 160 | 161 | Map vars = new HashMap<>(); 162 | vars.put("component", get("fieldsContainer").getMarkupId()); 163 | vars.put("stopBehavior", script.toString()); 164 | 165 | PackageTextTemplate template = new PackageTextTemplate(PivotAreaPanel.class, "res/sort-behavior.template.js"); 166 | template.interpolate(vars); 167 | 168 | response.render(OnDomReadyHeaderItem.forScript(template.getString())); 169 | try { 170 | template.close(); 171 | } catch(IOException e) { 172 | throw new RuntimeException(e); 173 | } 174 | } 175 | 176 | public ListView getFieldsView() { 177 | return fieldsView; 178 | } 179 | 180 | private void addSortableBehavior(Component component) { 181 | sortableAjaxBehavior = new SortableAjaxBehavior() { 182 | 183 | private static final long serialVersionUID = 1L; 184 | 185 | @Override 186 | public void onSort(AjaxRequestTarget target, Item[] items) { 187 | PivotModel pivotModel = getPivotModel(); 188 | for (Item item : items) { 189 | PivotField pivotField = pivotModel.getField(item.fieldIndex); 190 | pivotField.setArea(PivotField.Area.getValue(item.areaName)); 191 | pivotField.setAreaIndex(item.sortIndex); 192 | } 193 | send(getPage(), Broadcast.BREADTH, new AreaChangedEvent(target)); 194 | } 195 | 196 | }; 197 | component.add(sortableAjaxBehavior); 198 | } 199 | 200 | private PivotModel getPivotModel() { 201 | return findParent(PivotPanel.class).getPivotModel(); 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/Aggregator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | import java.util.Arrays; 17 | import java.util.Collection; 18 | import java.util.Collections; 19 | import java.util.List; 20 | 21 | /** 22 | * @author Decebal Suiu 23 | */ 24 | public abstract class Aggregator implements Serializable { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | public static final String SUM = "sum"; 29 | public static final String AVG = "avg"; 30 | public static final String MIN = "min"; 31 | public static final String MAX = "max"; 32 | public static final String COUNT = "count"; 33 | 34 | public static final List FUNCTIONS = Collections.unmodifiableList(Arrays.asList(SUM, AVG, MIN, MAX, COUNT)); 35 | 36 | public static List getFunctions() { 37 | return FUNCTIONS; 38 | } 39 | 40 | public static Aggregator get(String function) { 41 | if (function.equalsIgnoreCase(SUM)) { 42 | return new Sum(); 43 | } else if (function.equalsIgnoreCase(AVG)) { 44 | return new Average(); 45 | } else if (function.equalsIgnoreCase(MIN)) { 46 | return new Minimum(); 47 | } else if (function.equalsIgnoreCase(MAX)) { 48 | return new Maximum(); 49 | } else if (function.equalsIgnoreCase(COUNT)) { 50 | return new Count(); 51 | } 52 | 53 | return null; 54 | } 55 | 56 | public abstract Aggregator init(); 57 | 58 | public abstract Aggregator add(Object value); 59 | 60 | public abstract Object getResult(); 61 | 62 | public abstract String getFunction(); 63 | 64 | public Aggregator addAll(Collection values) { 65 | for (Object value : values) { 66 | add(value); 67 | } 68 | 69 | return this; 70 | } 71 | 72 | @Override 73 | public boolean equals(Object obj) { 74 | if (obj instanceof Aggregator) { 75 | String tmp = ((Aggregator) obj).getFunction(); 76 | return getFunction().equals(tmp); 77 | } 78 | 79 | return false; 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return getFunction().hashCode(); 85 | } 86 | 87 | public static class Sum extends Aggregator { 88 | 89 | private static final long serialVersionUID = 1L; 90 | 91 | private double total; 92 | 93 | @Override 94 | public Aggregator init() { 95 | total = 0.0; 96 | 97 | return this; 98 | } 99 | 100 | @Override 101 | public Aggregator add(Object value) { 102 | if (value instanceof Number) { 103 | total += ((Number) value).doubleValue(); 104 | } 105 | 106 | return this; 107 | } 108 | 109 | @Override 110 | public Object getResult() { 111 | return total; 112 | } 113 | 114 | @Override 115 | public String getFunction() { 116 | return SUM; 117 | } 118 | 119 | } 120 | 121 | public static class Average extends Aggregator { 122 | 123 | private static final long serialVersionUID = 1L; 124 | 125 | private double total; 126 | private double count; 127 | 128 | @Override 129 | public Aggregator init() { 130 | total = 0.0; 131 | count = 0.0; 132 | 133 | return this; 134 | } 135 | 136 | @Override 137 | public Aggregator add(Object value) { 138 | if (value instanceof Number) { 139 | total += ((Number) value).doubleValue(); 140 | count++; 141 | } 142 | 143 | return this; 144 | } 145 | 146 | @Override 147 | public Object getResult() { 148 | if (count == 0.0) { 149 | return null; 150 | } 151 | 152 | return total / count; 153 | } 154 | 155 | @Override 156 | public String getFunction() { 157 | return AVG; 158 | } 159 | 160 | } 161 | 162 | public static class Minimum extends Aggregator { 163 | 164 | private static final long serialVersionUID = 1L; 165 | 166 | private Object min; 167 | 168 | @Override 169 | public Aggregator init() { 170 | min = null; 171 | 172 | return this; 173 | } 174 | 175 | @Override 176 | @SuppressWarnings("unchecked") 177 | public Aggregator add(Object value) { 178 | if (value != null) { 179 | if (min == null) { 180 | min = value; 181 | } else if (value instanceof Comparable) { 182 | if (((Comparable) value).compareTo(min) < 0) { 183 | min = value; 184 | } 185 | } 186 | } 187 | 188 | return this; 189 | } 190 | 191 | @Override 192 | public Object getResult() { 193 | return min; 194 | } 195 | 196 | @Override 197 | public String getFunction() { 198 | return MIN; 199 | } 200 | 201 | } 202 | 203 | public static class Maximum extends Aggregator { 204 | 205 | private static final long serialVersionUID = 1L; 206 | 207 | private Object max; 208 | 209 | @Override 210 | public Aggregator init() { 211 | max = null; 212 | 213 | return this; 214 | } 215 | 216 | @Override 217 | @SuppressWarnings("unchecked") 218 | public Aggregator add(Object value) { 219 | if (value != null) { 220 | if (max == null) { 221 | max = value; 222 | } else if (value instanceof Comparable) { 223 | if (((Comparable) value).compareTo(max) > 0) { 224 | max = value; 225 | } 226 | } 227 | } 228 | 229 | return this; 230 | } 231 | 232 | @Override 233 | public Object getResult() { 234 | return max; 235 | } 236 | 237 | @Override 238 | public String getFunction() { 239 | return MAX; 240 | } 241 | 242 | } 243 | 244 | public static class Count extends Aggregator { 245 | 246 | private static final long serialVersionUID = 1L; 247 | 248 | private int count; 249 | 250 | @Override 251 | public Aggregator init() { 252 | count = 0; 253 | 254 | return this; 255 | } 256 | 257 | @Override 258 | public Aggregator add(Object value) { 259 | if (value != null) { 260 | count++; 261 | } 262 | 263 | return this; 264 | } 265 | 266 | @Override 267 | public Object getResult() { 268 | return count; 269 | } 270 | 271 | @Override 272 | public String getFunction() { 273 | return COUNT; 274 | } 275 | 276 | } 277 | 278 | } 279 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/PivotFieldAction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import java.io.Serializable; 16 | 17 | import org.apache.wicket.AttributeModifier; 18 | import org.apache.wicket.Component; 19 | import org.apache.wicket.ajax.AjaxRequestTarget; 20 | import org.apache.wicket.ajax.markup.html.AjaxLink; 21 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 22 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.WindowClosedCallback; 23 | import org.apache.wicket.markup.html.link.AbstractLink; 24 | import org.apache.wicket.model.Model; 25 | 26 | import ro.fortsoft.wicket.pivot.PivotField.Area; 27 | import ro.fortsoft.wicket.pivot.web.AggregatorPanel; 28 | import ro.fortsoft.wicket.pivot.web.FieldCalculationPanel; 29 | import ro.fortsoft.wicket.pivot.web.PivotAreaPanel; 30 | import ro.fortsoft.wicket.pivot.web.PivotPanel; 31 | 32 | /** 33 | * @author Decebal Suiu 34 | */ 35 | public abstract class PivotFieldAction implements Serializable { 36 | 37 | private static final long serialVersionUID = 1L; 38 | 39 | protected PivotField field; 40 | protected String name; 41 | 42 | // protected IResource image; 43 | // protected String tooltip; 44 | 45 | public PivotFieldAction(PivotField field) { 46 | this.field = field; 47 | } 48 | 49 | public abstract AbstractLink getLink(String id); 50 | 51 | public String getName() { 52 | return name; 53 | } 54 | 55 | /* 56 | * public IResource getImage() { return image; } 57 | * 58 | * public String getTooltip() { return tooltip; } 59 | */ 60 | 61 | public static class Delete extends PivotFieldAction { 62 | 63 | private static final long serialVersionUID = 1L; 64 | 65 | public Delete(PivotField field) { 66 | super(field); 67 | 68 | name = "Delete"; 69 | // image = new ContextRelativeResource("images/delete.gif"); 70 | // tooltip = "Delete"; 71 | } 72 | 73 | @Override 74 | public AbstractLink getLink(String id) { 75 | return new AjaxLink(id) { 76 | 77 | private static final long serialVersionUID = 1L; 78 | 79 | @Override 80 | public void onClick(AjaxRequestTarget target) { 81 | // TODO: implement 82 | } 83 | 84 | }; 85 | } 86 | 87 | } 88 | 89 | public static class FieldCalculationAction extends PivotFieldAction { 90 | 91 | private PivotModel pivotModel; 92 | 93 | public FieldCalculationAction(PivotField field, PivotModel pivotModel) { 94 | super(field); 95 | this.pivotModel = pivotModel; 96 | name = "Calculation..."; 97 | } 98 | 99 | private PivotModel getPivotModel(Component webComponent) { 100 | return webComponent.findParent(PivotPanel.class).getPivotModel(); 101 | } 102 | 103 | private static final long serialVersionUID = 1L; 104 | 105 | private void openFieldCalculationDialog(final AjaxLink ajaxLink, AjaxRequestTarget target) { 106 | ModalWindow modal = ajaxLink.findParent(PivotAreaPanel.class).getModal(); 107 | modal.setTitle("Calculation"); 108 | final FieldCalculationPanel panel = new FieldCalculationPanel(modal.getContentId(), Model.of(field), 109 | Model.of(pivotModel)); 110 | panel.setTitle(field.getTitle()); 111 | panel.add(AttributeModifier.append("style", "padding: 10px;")); 112 | modal.setContent(panel); 113 | modal.setAutoSize(true); 114 | modal.setResizable(false); 115 | modal.show(target); 116 | modal.setWindowClosedCallback(new WindowClosedCallback() { 117 | 118 | private static final long serialVersionUID = 1L; 119 | 120 | @Override 121 | public void onClose(AjaxRequestTarget target) { 122 | if (!panel.isOkPressed()) { 123 | return; 124 | } 125 | 126 | target.add(ajaxLink.findParent(PivotAreaPanel.class)); 127 | 128 | PivotModel pivotModel = getPivotModel(ajaxLink); 129 | PivotField pivotField = pivotModel.getField(field.getName()); 130 | pivotField.setFieldCalculation(panel.getFieldCalculation()); 131 | pivotField.setTitle(panel.getTitle()); 132 | if (pivotModel.isAutoCalculate()) { 133 | ajaxLink.findParent(PivotPanel.class).compute(target); 134 | } 135 | } 136 | }); 137 | } 138 | 139 | @Override 140 | public AbstractLink getLink(String id) { 141 | return new AjaxLink(id) { 142 | 143 | private static final long serialVersionUID = 1L; 144 | 145 | @Override 146 | public void onClick(AjaxRequestTarget target) { 147 | openFieldCalculationDialog(this, target); 148 | } 149 | 150 | }; 151 | } 152 | } 153 | 154 | public static class AddNewFieldCalculationAction extends PivotFieldAction { 155 | private static final long serialVersionUID = 1L; 156 | private PivotModel model; 157 | 158 | public AddNewFieldCalculationAction(PivotField field, PivotModel model) { 159 | super(field); 160 | this.model = model; 161 | name = "Add calculation ..."; 162 | } 163 | 164 | @Override 165 | public AbstractLink getLink(String id) { 166 | return new AjaxLink(id) { 167 | private static final long serialVersionUID = 1L; 168 | 169 | @Override 170 | public void onClick(AjaxRequestTarget target) { 171 | PivotField newField = new PivotField("", model.getFields().size()); 172 | newField.setArea(Area.DATA); 173 | newField.setType(Double.class); 174 | newField.setTitle("Calculation"); 175 | newField.setAggregator(null); 176 | newField.setFieldCalculation(FieldCalculation.get(FieldCalculation.PERCENT_OF)); 177 | newField.getFieldCalculation().setFieldA(field); 178 | newField.getFieldCalculation().setFieldB(field); 179 | model.getFields().add(newField); 180 | new FieldCalculationAction(newField, model).openFieldCalculationDialog(this, target); 181 | } 182 | }; 183 | } 184 | } 185 | 186 | public static class AggregatorAction extends PivotFieldAction { 187 | 188 | private static final long serialVersionUID = 1L; 189 | 190 | public AggregatorAction(PivotField field) { 191 | super(field); 192 | 193 | name = "Aggregator..."; 194 | // image = new ContextRelativeResource("images/agregator.gif"); 195 | // tooltip = "Delete"; 196 | } 197 | 198 | @Override 199 | public AbstractLink getLink(String id) { 200 | return new AjaxLink(id) { 201 | 202 | private static final long serialVersionUID = 1L; 203 | 204 | @Override 205 | public void onClick(AjaxRequestTarget target) { 206 | ModalWindow modal = findParent(PivotAreaPanel.class).getModal(); 207 | modal.setTitle("Aggregator"); 208 | final AggregatorPanel panel = new AggregatorPanel(modal.getContentId(), Model.of(field)); 209 | panel.add(AttributeModifier.append("style", "padding: 10px;")); 210 | modal.setContent(panel); 211 | modal.setAutoSize(true); 212 | modal.setResizable(false); 213 | modal.show(target); 214 | modal.setWindowClosedCallback(new WindowClosedCallback() { 215 | 216 | private static final long serialVersionUID = 1L; 217 | 218 | @Override 219 | public void onClose(AjaxRequestTarget target) { 220 | if (!panel.isOkPressed()) { 221 | return; 222 | } 223 | 224 | target.add(findParent(PivotAreaPanel.class)); 225 | PivotModel pivotModel = getPivotModel(); 226 | pivotModel.getField(field.getName()).setAggregator(panel.getAggregator()); 227 | if (pivotModel.isAutoCalculate()) { 228 | findParent(PivotPanel.class).compute(target); 229 | } 230 | 231 | } 232 | }); 233 | } 234 | 235 | private PivotModel getPivotModel() { 236 | return findParent(PivotPanel.class).getPivotModel(); 237 | } 238 | 239 | }; 240 | } 241 | 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotTable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import java.io.Serializable; 16 | 17 | import org.apache.wicket.AttributeModifier; 18 | import org.apache.wicket.Component; 19 | import org.apache.wicket.markup.html.WebMarkupContainer; 20 | import org.apache.wicket.markup.html.basic.Label; 21 | import org.apache.wicket.markup.html.panel.GenericPanel; 22 | import org.apache.wicket.markup.repeater.RepeatingView; 23 | import org.apache.wicket.model.Model; 24 | import org.apache.wicket.util.convert.IConverter; 25 | 26 | import ro.fortsoft.wicket.pivot.PivotField; 27 | import ro.fortsoft.wicket.pivot.PivotModel; 28 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel; 29 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.DataHeaderRenderCell; 30 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.DataRenderRow; 31 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.DataValueRenderCell; 32 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.GrandTotalHeaderRenderCell; 33 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.GrandTotalRenderRow; 34 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.GrandTotalRowHeaderRenderCell; 35 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.GrandTotalValueRenderCell; 36 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.HeaderRenderCell; 37 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.HeaderRenderRow; 38 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.HeaderValueRenderCell; 39 | import ro.fortsoft.wicket.pivot.PivotTableRenderModel.RenderCell; 40 | 41 | /** 42 | * @author Decebal Suiu 43 | */ 44 | public class PivotTable extends GenericPanel { 45 | 46 | private static final long serialVersionUID = 1L; 47 | 48 | public PivotTable(String id, PivotModel pivotModel) { 49 | super(id, Model.of(pivotModel)); 50 | } 51 | 52 | private Component applyRowColSpan(RenderCell cell, Component tmp) { 53 | if (cell.getColspan() > 1) 54 | tmp.add(AttributeModifier.append("colspan", cell.getColspan())); 55 | if (cell.getRowspan() > 1) 56 | tmp.add(AttributeModifier.append("rowspan", cell.getRowspan())); 57 | return tmp; 58 | } 59 | 60 | @Override 61 | protected void onInitialize() { 62 | super.onInitialize(); 63 | 64 | PivotModel pivotModel = getModelObject(); 65 | PivotTableRenderModel renderModel = PivotTableRenderModel.create(pivotModel); 66 | 67 | // rendering header 68 | RepeatingView column = new RepeatingView("header"); 69 | add(column); 70 | 71 | Component tmp = null; 72 | for (HeaderRenderRow row : renderModel.getHeaderRows()) { 73 | // rendering row header (first columns) 74 | WebMarkupContainer tr = new WebMarkupContainer(column.newChildId()); 75 | column.add(tr); 76 | RepeatingView rowHeader = new RepeatingView("rowHeader"); 77 | tr.add(rowHeader); 78 | 79 | for (HeaderRenderCell cell : row.getRowHeader()) { 80 | if (cell.getPivotField() == null) { 81 | // rendering an empty cell 82 | tmp = new Label(rowHeader.newChildId(), ""); 83 | tmp.add(AttributeModifier.append("class", "empty")); 84 | applyRowColSpan(cell, tmp); 85 | rowHeader.add(tmp); 86 | } else { 87 | // rendering row field 88 | tmp = createTitleLabel(rowHeader.newChildId(), cell.getPivotField()); 89 | applyRowColSpan(cell, tmp); 90 | rowHeader.add(tmp); 91 | } 92 | } 93 | 94 | // rendering column keys 95 | RepeatingView value = new RepeatingView("value"); 96 | tr.add(value); 97 | for (RenderCell cell : row.getValueCells()) { 98 | if (cell instanceof HeaderValueRenderCell) { 99 | HeaderValueRenderCell headerValueRenderCell = (HeaderValueRenderCell) cell; 100 | tmp = createValueLabel(value.newChildId(), headerValueRenderCell.getRawValue(), 101 | headerValueRenderCell.getPivotField()); 102 | applyRowColSpan(cell, tmp); 103 | value.add(tmp); 104 | } else { 105 | HeaderRenderCell headerRenderCell = (HeaderRenderCell) cell; 106 | tmp = createTitleLabel(value.newChildId(), headerRenderCell.getPivotField()); 107 | applyRowColSpan(cell, tmp); 108 | value.add(tmp); 109 | } 110 | } 111 | 112 | // rendering grand total column 113 | RepeatingView grandTotalColumn = new RepeatingView("grandTotalColumn"); 114 | for (RenderCell cell : row.getGrandTotalColumn()) { 115 | if (cell instanceof GrandTotalHeaderRenderCell) { 116 | GrandTotalHeaderRenderCell grandTotalHeaderRenderCell = (GrandTotalHeaderRenderCell) cell; 117 | if (grandTotalHeaderRenderCell.getRawValue() != null) { 118 | tmp = new Label(grandTotalColumn.newChildId(), grandTotalHeaderRenderCell.getRawValue() 119 | .toString()); 120 | applyRowColSpan(cell, tmp); 121 | grandTotalColumn.add(tmp); 122 | } else { 123 | tmp = new WebMarkupContainer(grandTotalColumn.newChildId()); 124 | applyRowColSpan(cell, tmp); 125 | tmp.add(AttributeModifier.append("class", "empty")); 126 | grandTotalColumn.add(tmp); 127 | } 128 | } else { 129 | HeaderRenderCell headerCell = (HeaderRenderCell) cell; 130 | tmp = createTitleLabel(value.newChildId(), headerCell.getPivotField()); 131 | applyRowColSpan(cell, tmp); 132 | grandTotalColumn.add(tmp); 133 | } 134 | } 135 | grandTotalColumn.setVisible(row.getGrandTotalColumn().size() > 0); 136 | tr.add(grandTotalColumn); 137 | } 138 | 139 | // rendering rows 140 | RepeatingView row = new RepeatingView("row"); 141 | add(row); 142 | for (DataRenderRow renderRow : renderModel.getValueRows()) { 143 | WebMarkupContainer tr = new WebMarkupContainer(row.newChildId()); 144 | row.add(tr); 145 | RepeatingView rowHeader = new RepeatingView("rowHeader"); 146 | tr.add(rowHeader); 147 | 148 | for (DataHeaderRenderCell cell : renderRow.getRowHeader()) { 149 | tmp = createValueLabel(rowHeader.newChildId(), cell.getRawValue(), cell.getPivotField()); 150 | applyRowColSpan(cell, tmp); 151 | rowHeader.add(tmp); 152 | } 153 | 154 | RepeatingView value = new RepeatingView("value"); 155 | tr.add(value); 156 | 157 | for (RenderCell cell : renderRow.getValue()) { 158 | if (cell instanceof DataValueRenderCell) { 159 | tmp = createValueLabel(value.newChildId(), cell.getRawValue(), cell.getPivotField()); 160 | applyRowColSpan(cell, tmp); 161 | value.add(tmp); 162 | } else { 163 | GrandTotalValueRenderCell grandTotalCell = (GrandTotalValueRenderCell) cell; 164 | tmp = createGrandTotalLabel(value.newChildId(), grandTotalCell.getRawValue(), grandTotalCell.isForRow()); 165 | applyRowColSpan(cell, tmp); 166 | tmp.add(AttributeModifier.append("class", "grand-total")); 167 | value.add(tmp); 168 | } 169 | } 170 | } 171 | 172 | WebMarkupContainer grandTotalRow = new WebMarkupContainer("grandTotalRow"); 173 | grandTotalRow.setVisible(renderModel.getGrandTotalRows().size() > 0); 174 | add(grandTotalRow); 175 | /* 176 | * We currently expect exactly one GrantTotalRenderRow, therefor we dont 177 | * need a repeating viewer 178 | */ 179 | for (GrandTotalRenderRow grantTotalRenderRow : renderModel.getGrandTotalRows()) { 180 | for (GrandTotalRowHeaderRenderCell cell : grantTotalRenderRow.getRowHeader()) { 181 | Label grandTotalRowHeader = new Label("rowHeader", "Grand Total"); 182 | applyRowColSpan(cell, grandTotalRowHeader); 183 | grandTotalRow.add(grandTotalRowHeader); 184 | } 185 | 186 | RepeatingView value = new RepeatingView("value"); 187 | grandTotalRow.add(value); 188 | for (GrandTotalValueRenderCell cell : grantTotalRenderRow.getValue()) { 189 | tmp = createGrandTotalLabel(value.newChildId(), cell.getRawValue(), cell.isForRow()); 190 | value.add(tmp); 191 | } 192 | } 193 | } 194 | 195 | /** 196 | * Retrieves a name that display the pivot table title (for fields on ROW 197 | * and DATA areas) 198 | */ 199 | protected Label createTitleLabel(String id, PivotField pivotField) { 200 | String title = pivotField.getTitle(); 201 | if (pivotField.getArea().equals(PivotField.Area.DATA)) { 202 | title += " (" + pivotField.getCalculationDescription() + ")"; 203 | } 204 | 205 | return new Label(id, title); 206 | } 207 | 208 | protected Label createValueLabel(String id, Object value, final PivotField pivotField) { 209 | return new Label(id, Model.of((Serializable) value)) { 210 | private static final long serialVersionUID = 1L; 211 | 212 | @SuppressWarnings("unchecked") 213 | @Override 214 | public IConverter getConverter(Class type) { 215 | IConverter converter = (IConverter) pivotField.getConverter(); 216 | if (converter != null) { 217 | return converter; 218 | } 219 | 220 | return super.getConverter(type); 221 | } 222 | 223 | }; 224 | } 225 | 226 | protected Label createGrandTotalLabel(String id, Object value, boolean forRow) { 227 | return new Label(id, Model.of((Serializable) value)); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/config/PivotConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, 2013, 2014 Decebal Suiu, Emmeran Seehuber 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.config; 14 | 15 | import ro.fortsoft.wicket.pivot.Aggregator; 16 | import ro.fortsoft.wicket.pivot.FieldCalculation; 17 | import ro.fortsoft.wicket.pivot.PivotField; 18 | import ro.fortsoft.wicket.pivot.PivotField.Area; 19 | import ro.fortsoft.wicket.pivot.PivotModel; 20 | 21 | import java.io.Serializable; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * A stored pivot table configuration. The structure of this class and its child 27 | * PivotConfigField is guaranteed to be stable across releases. Serialize this 28 | * class using the Java serializers or whatever you like (e.g. GSON) 29 | * 30 | */ 31 | public class PivotConfig implements Serializable { 32 | private static final long serialVersionUID = 1L; 33 | 34 | /** 35 | * Name of the configuration, to show in the UI. 36 | */ 37 | private String name; 38 | private boolean showGrandTotalForColumn; 39 | private boolean showGrandTotalForRow; 40 | private boolean autoCalculate; 41 | 42 | /** 43 | * The fields are stored as an array - so that this can be correctly be 44 | * stored/restored with GSON 45 | */ 46 | private PivotConfigField[] pivotConfigFields = new PivotConfigField[0]; 47 | 48 | /** 49 | * Represents the configuration state of a PivotField 50 | */ 51 | public static class PivotConfigField implements Serializable { 52 | private static final long serialVersionUID = 1L; 53 | private String name; 54 | private String title; 55 | private Area area; 56 | private int areaIndex; 57 | private String aggreatorFunction; 58 | private String fieldCalculationFunction; 59 | private String[] fieldCalculationFields; 60 | private int sortOrder; 61 | 62 | public String getName() { 63 | return name; 64 | } 65 | 66 | public void setName(String name) { 67 | this.name = name; 68 | } 69 | 70 | public Area getArea() { 71 | return area; 72 | } 73 | 74 | public void setArea(Area area) { 75 | this.area = area; 76 | } 77 | 78 | public int getAreaIndex() { 79 | return areaIndex; 80 | } 81 | 82 | public void setAreaIndex(int areaIndex) { 83 | this.areaIndex = areaIndex; 84 | } 85 | 86 | public String getAggreatorFunction() { 87 | return aggreatorFunction; 88 | } 89 | 90 | public void setAggreatorFunction(String aggreatorFunction) { 91 | this.aggreatorFunction = aggreatorFunction; 92 | } 93 | 94 | public String getFieldCalculationFunction() { 95 | return fieldCalculationFunction; 96 | } 97 | 98 | public void setFieldCalculationFunction(String fieldCalculationFunction) { 99 | this.fieldCalculationFunction = fieldCalculationFunction; 100 | } 101 | 102 | public String[] getFieldCalculationFields() { 103 | return fieldCalculationFields; 104 | } 105 | 106 | public void setFieldCalculationFields(String[] fieldCalculationFields) { 107 | this.fieldCalculationFields = fieldCalculationFields; 108 | } 109 | 110 | public int getSortOrder() { 111 | return sortOrder; 112 | } 113 | 114 | public void setSortOrder(int sortOrder) { 115 | this.sortOrder = sortOrder; 116 | } 117 | 118 | public String getTitle() { 119 | return title; 120 | } 121 | 122 | public void setTitle(String title) { 123 | this.title = title; 124 | } 125 | 126 | /** 127 | * Store the state of the of the pivot field in this instance 128 | * 129 | * @param field 130 | */ 131 | public void storeFieldState(PivotField field) { 132 | /* Plain Fields */ 133 | this.name = field.getName(); 134 | this.title = field.getTitle(); 135 | this.area = field.getArea(); 136 | this.areaIndex = field.getAreaIndex(); 137 | this.aggreatorFunction = null; 138 | this.fieldCalculationFunction = null; 139 | this.fieldCalculationFields = null; 140 | this.sortOrder = field.getSortOrder(); 141 | 142 | /* Functions */ 143 | if (field.getAggregator() != null) 144 | this.aggreatorFunction = field.getAggregator().getFunction(); 145 | if (field.getFieldCalculation() != null) { 146 | this.fieldCalculationFunction = field.getFieldCalculation().getFunction(); 147 | this.fieldCalculationFields = new String[] { getFieldName(field.getFieldCalculation().getFieldA()), 148 | getFieldName(field.getFieldCalculation().getFieldB()) }; 149 | } 150 | } 151 | 152 | private static String getFieldName(PivotField pivotField) { 153 | if (pivotField == null) 154 | return null; 155 | return pivotField.getName(); 156 | } 157 | 158 | /** 159 | * Restore the state of the pivot field from this instance. 160 | * 161 | * The name *MUST* be equal! 162 | */ 163 | public void restoreFieldState(PivotField field, PivotModel pivotModel) { 164 | if (!field.getName().equals(name)) 165 | throw new IllegalArgumentException(field.getName() + " != " + name); 166 | /* Plain Properties */ 167 | field.setTitle(this.title); 168 | field.setArea(this.getArea()); 169 | field.setAreaIndex(this.getAreaIndex()); 170 | field.setAggregator(null); 171 | field.setFieldCalculation(null); 172 | field.setSortOrder(this.getSortOrder()); 173 | 174 | /* Functions */ 175 | if (this.aggreatorFunction != null) 176 | field.setAggregator(Aggregator.get(this.aggreatorFunction)); 177 | if (this.fieldCalculationFunction != null) { 178 | FieldCalculation fieldCalculation = FieldCalculation.get(this.fieldCalculationFunction); 179 | field.setFieldCalculation(fieldCalculation); 180 | fieldCalculation.setFieldA(findField(this.fieldCalculationFields, 0, pivotModel)); 181 | fieldCalculation.setFieldB(findField(this.fieldCalculationFields, 1, pivotModel)); 182 | } 183 | } 184 | 185 | private static PivotField findField(String[] fieldCalculationFields, int index, PivotModel pivotModel) { 186 | if (fieldCalculationFields == null) 187 | return null; 188 | if (index >= fieldCalculationFields.length) 189 | return null; 190 | String fieldName = fieldCalculationFields[index]; 191 | if (fieldName == null) 192 | return null; 193 | return pivotModel.getField(fieldName); 194 | } 195 | } 196 | 197 | public String getName() { 198 | return name; 199 | } 200 | 201 | public void setName(String name) { 202 | this.name = name; 203 | } 204 | 205 | /* 206 | * @internal Only for the serializers 207 | */ 208 | public PivotConfigField[] getPivotConfigFields() { 209 | return pivotConfigFields; 210 | } 211 | 212 | /* 213 | * @internal Only for the serializers 214 | */ 215 | public void setPivotConfigFields(PivotConfigField[] pivotConfigFields) { 216 | this.pivotConfigFields = pivotConfigFields; 217 | } 218 | 219 | public boolean isShowGrandTotalForColumn() { 220 | return showGrandTotalForColumn; 221 | } 222 | 223 | public void setShowGrandTotalForColumn(boolean showGrandTotalForColumn) { 224 | this.showGrandTotalForColumn = showGrandTotalForColumn; 225 | } 226 | 227 | public boolean isShowGrandTotalForRow() { 228 | return showGrandTotalForRow; 229 | } 230 | 231 | public void setShowGrandTotalForRow(boolean showGrandTotalForRow) { 232 | this.showGrandTotalForRow = showGrandTotalForRow; 233 | } 234 | 235 | public boolean isAutoCalculate() { 236 | return autoCalculate; 237 | } 238 | 239 | public void setAutoCalculate(boolean autoCalculate) { 240 | this.autoCalculate = autoCalculate; 241 | } 242 | 243 | /** 244 | * Store the model state in this configuration instance 245 | * 246 | * @param model 247 | */ 248 | public void storeModelState(PivotModel model) { 249 | this.showGrandTotalForColumn = model.isShowGrandTotalForColumn(); 250 | this.showGrandTotalForRow = model.isShowGrandTotalForRow(); 251 | this.autoCalculate = model.isAutoCalculate(); 252 | 253 | List fields = new ArrayList<>(); 254 | for (PivotField field : model.getFields()) { 255 | /* We don't store unused fields */ 256 | if (field.getArea() == Area.UNUSED) 257 | continue; 258 | 259 | PivotConfigField configField = new PivotConfigField(); 260 | configField.storeFieldState(field); 261 | fields.add(configField); 262 | } 263 | pivotConfigFields = fields.toArray(new PivotConfigField[fields.size()]); 264 | } 265 | 266 | public void restoreModelState(PivotModel model) { 267 | /* 268 | * Global model state 269 | */ 270 | model.setShowGrandTotalForColumn(this.showGrandTotalForColumn); 271 | model.setShowGrandTotalForRow(this.showGrandTotalForRow); 272 | model.setAutoCalculate(this.autoCalculate); 273 | 274 | /* 275 | * We set all fields to unused, as we don't save unused fields, so thats 276 | * the default value 277 | */ 278 | List fieldsToDelete = new ArrayList<>(); 279 | for (PivotField field : model.getFields()) { 280 | /* 281 | * We must delete all field calculations, as this are additional 282 | * fields, which the user can add. Otherwise they will grow when we 283 | * load the same configuration with field calculations again and 284 | * again 285 | */ 286 | if (field.getFieldCalculation() != null) 287 | fieldsToDelete.add(field); 288 | field.setArea(Area.UNUSED); 289 | } 290 | 291 | /* 292 | * Now delete all field calculation fields 293 | */ 294 | for (PivotField field : fieldsToDelete) 295 | model.getFields().remove(field); 296 | 297 | /* 298 | * And restore the fields 299 | */ 300 | for (PivotConfigField configField : this.pivotConfigFields) { 301 | PivotField field = null; 302 | if (configField.getFieldCalculationFunction() != null) { 303 | /* 304 | * Special field for field calculation 305 | */ 306 | field = new PivotField("", model.getFields().size()); 307 | field.setType(Double.class); 308 | model.getFields().add(field); 309 | } else 310 | field = model.getField(configField.getName()); 311 | 312 | /* 313 | * Has the field been delete from the model? 314 | */ 315 | if (field == null) 316 | /* 317 | * Yes, ignore it 318 | */ 319 | continue; 320 | configField.restoreFieldState(field, model); 321 | } 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/DefaultPivotModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot; 14 | 15 | import org.apache.commons.collections.CollectionUtils; 16 | import org.apache.commons.collections.map.MultiKeyMap; 17 | import ro.fortsoft.wicket.pivot.tree.Node; 18 | import ro.fortsoft.wicket.pivot.tree.Tree; 19 | import ro.fortsoft.wicket.pivot.tree.TreeHelper; 20 | 21 | import java.util.*; 22 | 23 | /** 24 | * @author Decebal Suiu 25 | */ 26 | public class DefaultPivotModel implements PivotModel { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | private PivotDataSource dataSource; 31 | private List fields; 32 | private Tree columnsHeaderTree; 33 | private Tree rowsHeaderTree; 34 | private List calculatedData; // or use a MultiValueMap from apache commons 35 | 36 | private boolean showGrandTotalForColumn; 37 | private boolean showGrandTotalForRow; 38 | private boolean autoCalculate; 39 | 40 | public DefaultPivotModel(PivotDataSource dataSource) { 41 | this.dataSource = dataSource; 42 | 43 | // init fields 44 | int count = dataSource.getFieldCount(); 45 | fields = new ArrayList<>(count); 46 | for (int i = 0; i < count; i++) { 47 | PivotField field = new PivotField(dataSource.getFieldName(i), i); 48 | field.setTitle(field.getName()); 49 | field.setArea(PivotField.Area.UNUSED); 50 | field.setType(dataSource.getFieldType(i)); 51 | fields.add(field); 52 | } 53 | } 54 | 55 | @Override 56 | public List getFields() { 57 | return fields; 58 | } 59 | 60 | @Override 61 | public PivotField getField(String name) { 62 | for (PivotField field : fields) { 63 | if (field.getName().equals(name)) { 64 | return field; 65 | } 66 | } 67 | 68 | return null; 69 | } 70 | 71 | @Override 72 | public PivotField getField(int index) { 73 | for (PivotField field : fields) { 74 | if (field.getIndex() == index) { 75 | return field; 76 | } 77 | } 78 | 79 | return null; 80 | } 81 | 82 | @Override 83 | public List getFields(PivotField.Area area) { 84 | List areaFields = new ArrayList<>(); 85 | List fields = getFields(); 86 | for (PivotField field : fields) { 87 | if (field.getArea().equals(area)) { 88 | areaFields.add(field); 89 | } 90 | } 91 | Collections.sort(areaFields); 92 | 93 | return areaFields; 94 | } 95 | 96 | @Override 97 | public PivotDataSource getDataSource() { 98 | return dataSource; 99 | } 100 | 101 | @Override 102 | public void calculate() { 103 | long start = System.currentTimeMillis(); 104 | rowsHeaderTree = null; 105 | columnsHeaderTree = null; 106 | getRowsHeaderTree(); 107 | long t1 = System.currentTimeMillis(); 108 | System.out.println("created rowsHeaderTree in " + (t1 - start)); 109 | getColumnsHeaderTree(); 110 | long t2 = System.currentTimeMillis(); 111 | System.out.println("created columnsHeaderTree in " + (t2 - t1)); 112 | 113 | t1 = System.currentTimeMillis(); 114 | List dataFields = getFields(PivotField.Area.DATA); 115 | calculatedData = new ArrayList<>(); 116 | for (PivotField field : dataFields) { 117 | field.resetCalculation(); 118 | calculatedData.add(getData(field)); 119 | } 120 | t2 = System.currentTimeMillis(); 121 | System.out.println("filled calculatedData in " + (t2 - t1)); 122 | long stop = System.currentTimeMillis(); 123 | System.out.println("calculated in " + (stop- start)); 124 | System.out.println("calculatedData = " + calculatedData); 125 | // getValues(field, filter) 126 | } 127 | 128 | /* 129 | * TODO: trebuie imbunatatita metoda asta. Am facut un test pe un tabel 130 | * cu 4500 inregistrari si 7 coloane (nextreports downloads). Am observat ca 131 | * la 86 chei pe row si 212 chei pe column am 18.232 (86 x 212) combinatii. 132 | * Daca in getValues se sta 3,25 ms (cum am obtinut) rezulta un total de 133 | * 5576 ms. Cred ca ar trebuii sa parcurg o singura data inregistrarile din baza. 134 | */ 135 | private MultiKeyMap getData(PivotField dataField) { 136 | MultiKeyMap data = new MultiKeyMap(); 137 | List> rowKeys = getRowKeys(); 138 | System.out.println("rowKeys.size() = " + rowKeys.size()); 139 | List> columnKeys = getColumnKeys(); 140 | System.out.println("columnKeys.size() = " + columnKeys.size()); 141 | 142 | List rowFields = getFields(PivotField.Area.ROW); 143 | List columnFields = getFields(PivotField.Area.COLUMN); 144 | for (List rowKey : rowKeys) { 145 | for (List columnKey : columnKeys) { 146 | Map rowFilter = getFilter(rowFields, rowKey); 147 | Map columnFilter = getFilter(columnFields, columnKey); 148 | final Map filter = new HashMap<>(rowFilter); 149 | filter.putAll(columnFilter); 150 | List values = getValues(dataField, filter); 151 | if (!CollectionUtils.isEmpty(values) || dataField.getFieldCalculation()!=null) { 152 | /* 153 | System.out.println("filter = " + filter); 154 | System.out.println("values = " + values); 155 | System.out.println(values.size()); 156 | */ 157 | Object summary = PivotUtils.getSummary(dataField, values, field -> { 158 | List fieldValues = getValues(field, filter); 159 | return field.getAggregator().init().addAll(fieldValues).getResult(); 160 | }); 161 | // System.out.println("summary = " + summary); 162 | data.put(rowKey, columnKey, summary); 163 | } 164 | } 165 | } 166 | 167 | return data; 168 | } 169 | 170 | @Override 171 | public Tree getColumnsHeaderTree() { 172 | if (columnsHeaderTree == null) { 173 | Node root = new Node(); 174 | insertChildren(root, getFields(PivotField.Area.COLUMN)); 175 | columnsHeaderTree = new Tree(root); 176 | } 177 | 178 | return columnsHeaderTree; 179 | } 180 | 181 | @Override 182 | public Tree getRowsHeaderTree() { 183 | if (rowsHeaderTree == null) { 184 | Node root = new Node(); 185 | insertChildren(root, getFields(PivotField.Area.ROW)); 186 | rowsHeaderTree = new Tree(root); 187 | } 188 | 189 | return rowsHeaderTree; 190 | } 191 | 192 | @Override 193 | public List> getRowKeys() { 194 | return TreeHelper.getLeafValues(getRowsHeaderTree().getRoot()); 195 | } 196 | 197 | @Override 198 | public List> getColumnKeys() { 199 | return TreeHelper.getLeafValues(getColumnsHeaderTree().getRoot()); 200 | } 201 | 202 | @Override 203 | public Object getValueAt(PivotField dataField, List rowKey, List columnKey) { 204 | int index = getFields(PivotField.Area.DATA).indexOf(dataField); 205 | return calculatedData.get(index).get(rowKey, columnKey); 206 | } 207 | 208 | @Override 209 | public boolean isShowGrandTotalForColumn() { 210 | return showGrandTotalForColumn; 211 | } 212 | 213 | @Override 214 | public void setShowGrandTotalForColumn(boolean showGrandTotalForColumn) { 215 | this.showGrandTotalForColumn = showGrandTotalForColumn; 216 | } 217 | 218 | @Override 219 | public boolean isShowGrandTotalForRow() { 220 | return showGrandTotalForRow; 221 | } 222 | 223 | @Override 224 | public void setShowGrandTotalForRow(boolean showGrandTotalForRow) { 225 | this.showGrandTotalForRow = showGrandTotalForRow; 226 | } 227 | 228 | @Override 229 | public boolean isAutoCalculate() { 230 | return autoCalculate; 231 | } 232 | 233 | @Override 234 | public void setAutoCalculate(boolean autoCalculate) { 235 | this.autoCalculate = autoCalculate; 236 | } 237 | 238 | @Override 239 | public String toString() { 240 | return "DefaultPivotModel [fields=" + fields + "]"; 241 | } 242 | 243 | private void insertChildren(Node node, List fields) { 244 | // System.out.println("DefaultPivotModel.insertChildren()"); 245 | Set values = getPossibleChildrenValues(node, fields); 246 | if (CollectionUtils.isEmpty(values)) { 247 | return; 248 | } 249 | 250 | for (Object value : values) { 251 | node.insert(value); 252 | } 253 | 254 | for (Node child : node.getChildren()) { 255 | insertChildren(child, fields); 256 | } 257 | } 258 | 259 | private Set getPossibleChildrenValues(Node node, List fields) { 260 | int level = node.getLevel(); 261 | // System.out.println("level = " + level); 262 | // System.out.println("fields.size = " + fields.size()); 263 | if (fields.size() <= level) { 264 | return null; 265 | } 266 | 267 | PivotField nextField = fields.get(level); 268 | // System.out.println("nextField = " + nextField); 269 | Map filter = getFilter(fields, node.getPathValues()); 270 | // System.out.println("filter = " + filter); 271 | Set values = getUniqueValues(nextField, filter); 272 | // System.out.println("values = " + values); 273 | 274 | return values; 275 | } 276 | 277 | /* 278 | * Retrieves the values for a data field using a filter. 279 | */ 280 | private List getValues(PivotField field, Map filter) { 281 | if (field.getFieldCalculation() != null) 282 | return Collections.emptyList(); 283 | // long start = System.currentTimeMillis(); 284 | List values = new ArrayList<>(); 285 | final int fieldIndex = field.getIndex(); 286 | final int rowCount = dataSource.getRowCount(); 287 | 288 | if (filter.isEmpty()) { 289 | /* 290 | * No filter -> Just add the values 291 | */ 292 | for (int i = 0; i < rowCount; i++) { 293 | values.add(dataSource.getValueAt(i, fieldIndex)); 294 | } 295 | } 296 | else { 297 | /* 298 | * Add all values matching the filter 299 | */ 300 | for (int i = 0; i < rowCount; i++) { 301 | if (acceptValue(i, filter)) { 302 | values.add(dataSource.getValueAt(i, fieldIndex)); 303 | } 304 | } 305 | } 306 | // long stop = System.currentTimeMillis(); 307 | // System.out.println("getValues in " + (stop - start)); 308 | 309 | return values; 310 | } 311 | 312 | /* 313 | * Retrieves a filter for filtering data source (raw data). The size of fields must be equals with 314 | * the size of values. The key in map is the field index. 315 | */ 316 | private Map getFilter(List fields, List values) { 317 | // long start = System.currentTimeMillis(); 318 | Map filter = new HashMap<>(); 319 | for (int i = 0; i < values.size(); i++) { 320 | int fieldIndex = fields.get(i).getIndex(); 321 | // System.out.println(fieldIndex); 322 | filter.put(fieldIndex, values.get(i)); 323 | } 324 | // long stop = System.currentTimeMillis(); 325 | // System.out.println("getFilter in " + (stop - start)); 326 | 327 | return filter; 328 | } 329 | 330 | private Set getUniqueValues(PivotField field, Map filter) { 331 | List values = getValues(field, filter); 332 | 333 | int sortOrder = field.getSortOrder(); 334 | if (sortOrder != PivotField.SORT_ORDER_UNSORTED) { 335 | /* 336 | * We need to get the value set and sort it. We can not use a 337 | * TreeSet here as it does not allow null values. 338 | */ 339 | Set valueSet = new HashSet<>(values); 340 | List valuesToOrder = new ArrayList<>(valueSet); 341 | final int sign = sortOrder == PivotField.SORT_ORDER_ASCENDING ? 1 342 | : sortOrder == PivotField.SORT_ORDER_DESCENDING ? -1 : 1; 343 | valuesToOrder.sort((o1, o2) -> { 344 | if (o1 == o2) 345 | return 0; 346 | if (o1 == null) 347 | return sign * -1; 348 | if (o2 == null) 349 | return sign; 350 | return sign * ((Comparable) o1).compareTo(o2); 351 | }); 352 | 353 | return new LinkedHashSet<>(valuesToOrder); 354 | } 355 | 356 | return new LinkedHashSet<>(values); 357 | } 358 | 359 | private boolean acceptValue(int row, Map filter) { 360 | boolean accept = true; 361 | Set keys = filter.keySet(); 362 | Object value = null; 363 | for (int index : keys) { 364 | value = dataSource.getValueAt(row, fields.get(index)); 365 | Object filterValue = filter.get(index); 366 | if (!Objects.equals(filterValue, value)) { 367 | return false; 368 | } 369 | } 370 | 371 | return accept; 372 | } 373 | 374 | } 375 | -------------------------------------------------------------------------------- /wicket-pivot/src/main/java/ro/fortsoft/wicket/pivot/web/PivotPanel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Decebal Suiu 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with 5 | * the License. You may obtain a copy of the License in the LICENSE file, or at: 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | */ 13 | package ro.fortsoft.wicket.pivot.web; 14 | 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.IOException; 17 | import java.util.List; 18 | 19 | import org.apache.wicket.AttributeModifier; 20 | import org.apache.wicket.ajax.AjaxRequestTarget; 21 | import org.apache.wicket.ajax.markup.html.AjaxLink; 22 | import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; 23 | import org.apache.wicket.event.IEvent; 24 | import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink; 25 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow; 26 | import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.WindowClosedCallback; 27 | import org.apache.wicket.markup.html.WebMarkupContainer; 28 | import org.apache.wicket.markup.html.basic.Label; 29 | import org.apache.wicket.markup.html.link.Link; 30 | import org.apache.wicket.markup.html.panel.GenericPanel; 31 | import org.apache.wicket.markup.repeater.RepeatingView; 32 | import org.apache.wicket.model.AbstractReadOnlyModel; 33 | import org.apache.wicket.model.IModel; 34 | import org.apache.wicket.model.Model; 35 | import org.apache.wicket.model.PropertyModel; 36 | import org.apache.wicket.model.StringResourceModel; 37 | import org.apache.wicket.request.cycle.RequestCycle; 38 | import org.apache.wicket.request.handler.resource.ResourceRequestHandler; 39 | import org.apache.wicket.request.resource.ByteArrayResource; 40 | 41 | import ro.fortsoft.wicket.pivot.DefaultPivotFieldActionsFactory; 42 | import ro.fortsoft.wicket.pivot.DefaultPivotModel; 43 | import ro.fortsoft.wicket.pivot.PivotDataSource; 44 | import ro.fortsoft.wicket.pivot.PivotField; 45 | import ro.fortsoft.wicket.pivot.PivotFieldActionsFactory; 46 | import ro.fortsoft.wicket.pivot.PivotModel; 47 | import ro.fortsoft.wicket.pivot.config.IPivotConfigStorage; 48 | import ro.fortsoft.wicket.pivot.exporter.PivotCsvExporter; 49 | import ro.fortsoft.wicket.pivot.exporter.PivotExporter; 50 | 51 | /** 52 | * @author Decebal Suiu 53 | */ 54 | public class PivotPanel extends GenericPanel { 55 | 56 | private final class ButtonCssClassModel extends AbstractReadOnlyModel { 57 | private static final long serialVersionUID = 1L; 58 | 59 | @Override 60 | public String getObject() { 61 | return verify() ? "btn-success" : "btn-success disabled"; 62 | } 63 | } 64 | 65 | private static final long serialVersionUID = 1L; 66 | 67 | private WebMarkupContainer areasContainer; 68 | private PivotModel pivotModel; 69 | private PivotTable pivotTable; 70 | private AjaxLink computeLink; 71 | private WebMarkupContainer downloadContainer; 72 | private PivotExporter[] pivotExporters = new PivotExporter[] { new PivotCsvExporter() }; 73 | // TODO: requires Serializable?! 74 | private PivotFieldActionsFactory pivotFieldActionsFactory; 75 | private String pivotExportFilename = "pivottable"; 76 | private IPivotConfigStorage pivotConfigStorage; 77 | 78 | private ModalWindow modal; 79 | 80 | public PivotPanel(String id, PivotDataSource pivotDataSource) { 81 | super(id, Model.of(pivotDataSource)); 82 | } 83 | 84 | @Override 85 | protected void onInitialize() { 86 | super.onInitialize(); 87 | 88 | // create a pivot model 89 | pivotModel = createPivotModel(getModelObject()); 90 | 91 | // create pivot field action factory 92 | pivotFieldActionsFactory = createPivotFieldActionsFactory(); 93 | 94 | pivotModel.calculate(); 95 | 96 | areasContainer = new WebMarkupContainer("areas"); 97 | areasContainer.setOutputMarkupId(true); 98 | add(areasContainer); 99 | 100 | RepeatingView areaRepeater = new RepeatingView("area"); 101 | areasContainer.add(areaRepeater); 102 | List areas = PivotField.Area.getValues(); 103 | for (PivotField.Area area : areas) { 104 | areaRepeater.add(new PivotAreaPanel(areaRepeater.newChildId(), area)); 105 | } 106 | 107 | pivotTable = createPivotTabel("pivotTable", pivotModel); 108 | add(pivotTable); 109 | 110 | modal = new ModalWindow("modal"); 111 | modal.setAutoSize(true); 112 | add(modal); 113 | 114 | AjaxLink configStoreButton = new AjaxLink("configStoreButton") { 115 | private static final long serialVersionUID = 1L; 116 | 117 | @Override 118 | public void onClick(AjaxRequestTarget target) { 119 | modal.setTitle("Configuration"); 120 | modal.setContent(new PivotConfigStoragePanel(ModalWindow.CONTENT_ID, pivotModel, pivotConfigStorage)); 121 | modal.setAutoSize(true); 122 | modal.show(target); 123 | modal.setWindowClosedCallback(new WindowClosedCallback() { 124 | private static final long serialVersionUID = 1L; 125 | 126 | @Override 127 | public void onClose(AjaxRequestTarget target) { 128 | target.add(PivotPanel.this); 129 | if (pivotModel.isAutoCalculate()) 130 | compute(target); 131 | computeLink.setVisible(!pivotModel.isAutoCalculate()); 132 | } 133 | }); 134 | } 135 | 136 | @Override 137 | public boolean isVisible() { 138 | return super.isVisible() && pivotConfigStorage != null; 139 | } 140 | }; 141 | add(configStoreButton); 142 | 143 | AjaxCheckBox showGrandTotalForColumnCheckBox = new AjaxCheckBox("showGrandTotalForColumn", 144 | new PropertyModel<>(this, "pivotModel.showGrandTotalForColumn")) { 145 | 146 | private static final long serialVersionUID = 1L; 147 | 148 | @Override 149 | protected void onUpdate(AjaxRequestTarget target) { 150 | if (pivotModel.isAutoCalculate()) { 151 | compute(target); 152 | } 153 | } 154 | 155 | }; 156 | add(showGrandTotalForColumnCheckBox); 157 | 158 | AjaxCheckBox showGrandTotalForRowCheckBox = new AjaxCheckBox("showGrandTotalForRow", 159 | new PropertyModel<>(this, "pivotModel.showGrandTotalForRow")) { 160 | 161 | private static final long serialVersionUID = 1L; 162 | 163 | @Override 164 | protected void onUpdate(AjaxRequestTarget target) { 165 | if (pivotModel.isAutoCalculate()) { 166 | compute(target); 167 | } 168 | } 169 | 170 | }; 171 | add(showGrandTotalForRowCheckBox); 172 | 173 | AjaxCheckBox autoCalculateCheckBox = new AjaxCheckBox("autoCalculate", new PropertyModel<>(this, 174 | "pivotModel.autoCalculate")) { 175 | 176 | private static final long serialVersionUID = 1L; 177 | 178 | @Override 179 | protected void onUpdate(AjaxRequestTarget target) { 180 | computeLink.setVisible(!pivotModel.isAutoCalculate()); 181 | target.add(computeLink); 182 | 183 | if (pivotModel.isAutoCalculate() && !pivotTable.isVisible()) { 184 | compute(target); 185 | } 186 | } 187 | 188 | }; 189 | add(autoCalculateCheckBox); 190 | 191 | computeLink = new IndicatingAjaxLink("compute") { 192 | 193 | private static final long serialVersionUID = 1L; 194 | 195 | @Override 196 | public void onClick(AjaxRequestTarget target) { 197 | compute(target); 198 | } 199 | 200 | /* 201 | * @Override public boolean isEnabled() { return verify(); } 202 | */ 203 | 204 | }; 205 | computeLink.setOutputMarkupPlaceholderTag(true); 206 | computeLink.add(AttributeModifier.append("class", new ButtonCssClassModel())); 207 | computeLink.setVisible(!pivotModel.isAutoCalculate()); 208 | add(computeLink); 209 | 210 | downloadContainer = new WebMarkupContainer("downloadContainer"); 211 | downloadContainer.setOutputMarkupPlaceholderTag(true); 212 | downloadContainer.setVisible(pivotTable.isVisible() && (pivotExporters.length > 0)); 213 | add(downloadContainer); 214 | 215 | RepeatingView downloadExports = new RepeatingView("downloadExport"); 216 | downloadContainer.add(downloadExports); 217 | for (final PivotExporter exporter : pivotExporters) { 218 | Link downloadLink = new Link(downloadExports.newChildId()) { 219 | private static final long serialVersionUID = 1L; 220 | 221 | @Override 222 | public void onClick() { 223 | pivotModel.calculate(); 224 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 225 | try { 226 | exporter.exportPivot(getPivotModel(), out); 227 | } catch (IOException e) { 228 | throw new RuntimeException(e); 229 | } 230 | ResourceRequestHandler downloadHandler = new ResourceRequestHandler(new ByteArrayResource( 231 | exporter.getFormatMimetype(), out.toByteArray(), pivotExportFilename + "." 232 | + exporter.getFilenameExtension()), null); 233 | RequestCycle.get().scheduleRequestHandlerAfterCurrent(downloadHandler); 234 | } 235 | }; 236 | downloadExports.add(downloadLink); 237 | IModel resourceModel = new StringResourceModel("downloadAs", downloadLink, Model.of(exporter 238 | .getFormatName())); 239 | downloadLink.add(new Label("label", resourceModel)); 240 | downloadLink.setOutputMarkupPlaceholderTag(true); 241 | downloadLink.add(AttributeModifier.append("class", new ButtonCssClassModel())); 242 | } 243 | 244 | add(new PivotResourcesBehavior()); 245 | if (pivotModel.isAutoCalculate()) { 246 | compute(null); 247 | } 248 | 249 | setOutputMarkupId(true); 250 | } 251 | 252 | @Override 253 | public void onEvent(IEvent event) { 254 | super.onEvent(event); 255 | 256 | if (event.getPayload() instanceof AreaChangedEvent) { 257 | AjaxRequestTarget target = ((AreaChangedEvent) event.getPayload()).getAjaxRequestTarget(); 258 | target.add(areasContainer); 259 | target.add(computeLink); 260 | 261 | if (pivotModel.isAutoCalculate()) { 262 | compute(target); 263 | } 264 | } 265 | } 266 | 267 | public PivotModel getPivotModel() { 268 | return pivotModel; 269 | } 270 | 271 | public PivotFieldActionsFactory getPivotFieldActionsFactory() { 272 | return pivotFieldActionsFactory; 273 | } 274 | 275 | public void compute(AjaxRequestTarget target) { 276 | if (!verify()) { 277 | return; 278 | } 279 | 280 | pivotModel.calculate(); 281 | PivotTable newPivotTable = new PivotTable("pivotTable", pivotModel); 282 | pivotTable.replaceWith(newPivotTable); 283 | pivotTable = newPivotTable; 284 | if (target != null) { 285 | // update pivot table 286 | target.add(pivotTable); 287 | 288 | // update download container visibility 289 | downloadContainer.setVisible(pivotTable.isVisible() && (pivotExporters.length > 0)); 290 | target.add(downloadContainer); 291 | } 292 | } 293 | 294 | protected PivotModel createPivotModel(PivotDataSource pivotDataSource) { 295 | PivotModel pivotModel = new DefaultPivotModel(pivotDataSource); 296 | 297 | // debug 298 | /* 299 | * Tree columnsHeaderTree = pivotModel.getColumnsHeaderTree(); 300 | * System.out.println("### Columns Header Tree ###"); 301 | * TreeHelper.printTree(columnsHeaderTree.getRoot()); 302 | * TreeHelper.printLeafValues(columnsHeaderTree.getRoot()); 303 | * 304 | * Tree rowsHeaderTree = pivotModel.getRowsHeaderTree(); 305 | * System.out.println("### Rows Header Tree ### "); 306 | * TreeHelper.printTree(rowsHeaderTree.getRoot()); 307 | * TreeHelper.printLeafValues(rowsHeaderTree.getRoot()); 308 | */ 309 | 310 | return pivotModel; 311 | } 312 | 313 | protected PivotTable createPivotTabel(String id, PivotModel pivotModel) { 314 | PivotTable pivotTable = new PivotTable(id, pivotModel); 315 | pivotTable.setOutputMarkupPlaceholderTag(true); 316 | pivotTable.setVisible(false); 317 | 318 | return pivotTable; 319 | } 320 | 321 | protected PivotFieldActionsFactory createPivotFieldActionsFactory() { 322 | return new DefaultPivotFieldActionsFactory(); 323 | } 324 | 325 | private boolean verify() { 326 | return !pivotModel.getFields(PivotField.Area.DATA).isEmpty() 327 | && (!pivotModel.getFields(PivotField.Area.COLUMN).isEmpty() || !pivotModel.getFields( 328 | PivotField.Area.ROW).isEmpty()); 329 | } 330 | 331 | /** 332 | * Set the pivot exporter to use 333 | */ 334 | public void setPivotExporters(PivotExporter[] pivotExporter) { 335 | this.pivotExporters = pivotExporter; 336 | } 337 | 338 | /** 339 | * Set the basename of the download files 340 | * 341 | * @param pivotExportFilename 342 | */ 343 | public void setPivotExportFilename(String pivotExportFilename) { 344 | this.pivotExportFilename = pivotExportFilename; 345 | } 346 | 347 | public IPivotConfigStorage getPivotConfigStorage() { 348 | return pivotConfigStorage; 349 | } 350 | 351 | /** 352 | * Set the Pivot Configuration Storage to use. By default no storage is set. 353 | * 354 | * @param pivotConfigStorage 355 | */ 356 | public void setPivotConfigStorage(IPivotConfigStorage pivotConfigStorage) { 357 | this.pivotConfigStorage = pivotConfigStorage; 358 | } 359 | } 360 | --------------------------------------------------------------------------------