001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.plugins.design.ui.page.client;
021
022 import com.google.gwt.event.dom.client.ClickEvent;
023 import com.google.gwt.event.dom.client.ClickHandler;
024 import com.google.gwt.event.dom.client.DoubleClickEvent;
025 import com.google.gwt.event.dom.client.DoubleClickHandler;
026 import com.google.gwt.i18n.client.Dictionary;
027 import com.google.gwt.user.client.Window;
028 import com.google.gwt.user.client.ui.*;
029 import org.sonar.gwt.Links;
030 import org.sonar.gwt.ui.Icons;
031
032 import java.util.LinkedList;
033 import java.util.List;
034
035 public class Dsm extends Composite {
036
037 /* STYLES */
038 public static final String DSM = "dsm";
039
040 public static final String HEADER = "htable";
041 public static final String HEADER_TITLE = "ht";
042 public static final String HEADER_SELECTED_SUFFIX = "s";
043 public static final String HEADER_INDICATOR = "hi";
044 public static final String HEADER_HIGHER_INDICATOR_SUFFIX = "h";
045 public static final String HEADER_LOWER_INDICATOR_SUFFIX = "l";
046
047 public static final String GRID = "gtable";
048 public static final String GRID_CELL_BOTTOM_LEFT = "cbl";
049 public static final String GRID_CELL_TOP_RIGHT = "ctr";
050 public static final String GRID_CELL_DIAGONAL = "cd";
051 public static final String GRID_CELL_SELECTION1_SUFFIX = "s1";
052 public static final String GRID_CELL_SELECTION2_SUFFIX = "s2";
053 public static final String GRID_CELL_COMB1_SUFFIX = "c1";
054 public static final String GRID_CELL_COMB2_SUFFIX = "c2";
055 public static final String[] GRID_SUFFIXES = {GRID_CELL_SELECTION1_SUFFIX, GRID_CELL_SELECTION2_SUFFIX, GRID_CELL_COMB1_SUFFIX, GRID_CELL_COMB2_SUFFIX};
056
057
058 private VerticalPanel dsm = new VerticalPanel();
059 private DsmData.Rows data;
060 private Label[][] cells;
061 private Label[] titles;
062 private Label[] indicators;
063 private List<Label> highlightedCells = new LinkedList<Label>();
064
065 public Dsm() {
066 dsm.setStylePrimaryName(DSM);
067 initWidget(dsm);
068 }
069
070 private Widget createLegend() {
071 Dictionary l10n = Dictionary.getDictionary("l10n");
072 HorizontalPanel legend = new HorizontalPanel();
073 legend.getElement().setId("dsmlegend");
074 legend.add(new HTML("<div class='square gray'> </div>"));
075 legend.add(new Label(l10n.get("design.legend.dependencies")));
076 legend.add(new HTML("<div class='space'></div>"));
077 legend.add(new HTML("<div class='square red'> </div> "));
078 legend.add(new Label(l10n.get("design.legend.cycles")));
079 legend.add(new HTML(" <div class='space'></div> "));
080 legend.add(new HTML("<div class='square green'></div> "));
081 legend.add(new Label(l10n.get("design.legend.uses")));
082 legend.add(new HTML("<div class='square blue'></div> "));
083 legend.add(new Label(l10n.get("design.legend.uses")));
084 legend.add(new HTML(" <div class='square yellow'></div>"));
085 return legend;
086 }
087
088 public void displayNoData() {
089 dsm.clear();
090 dsm.add(new Label(Dictionary.getDictionary("l10n").get("noData")));
091 }
092
093 public void display(DsmData.Rows data) {
094 if (data == null) {
095 displayNoData();
096
097 } else {
098 this.data = data;
099 dsm.clear();
100 dsm.add(createHelp());
101 dsm.add(createLegend());
102 HorizontalPanel matrix = new HorizontalPanel();
103 matrix.add(createRowHeader());
104 matrix.add(createGrid());
105 dsm.add(matrix);
106 }
107 }
108
109 private Widget createHelp() {
110 HorizontalPanel help = new HorizontalPanel();
111 help.getElement().setId("dsmhelp");
112 Dictionary l10n = Dictionary.getDictionary("l10n");
113 Anchor link = new Anchor(l10n.get("design.help"), "http://docs.codehaus.org/x/QQFhC", "docsonar");
114 help.add(Icons.get().help().createImage());
115 help.add(link);
116 return help;
117 }
118
119 private Grid createRowHeader() {
120 Grid header = new Grid(data.size(), 2);
121 header.setCellPadding(0);
122 header.setCellSpacing(0);
123 header.setStylePrimaryName(HEADER);
124
125 titles = new Label[data.size()];
126 indicators = new Label[data.size()];
127 for (int indexRow = 0; indexRow < data.size(); indexRow++) {
128 DsmData.Row row = data.get(indexRow);
129
130 HTML title = buildRowTitle(indexRow, row);
131 titles[indexRow] = title;
132 header.setWidget(indexRow, 0, title);
133
134 Label indicator = buildLabel("", HEADER_INDICATOR);
135 header.setWidget(indexRow, 1, indicator);
136 indicators[indexRow] = indicator;
137 }
138 return header;
139 }
140
141 private Grid createGrid() {
142 int rowsCount = data.size();
143 Grid grid = new Grid(rowsCount, rowsCount);
144 grid.setCellPadding(0);
145 grid.setCellSpacing(0);
146 grid.setStylePrimaryName(GRID);
147
148 return loadGridCells(grid, data);
149 }
150
151 private Grid loadGridCells(Grid grid, DsmData.Rows data) {
152 int size = data.size();
153 cells = new Label[size][size];
154 for (int row = 0; row < size; row++) {
155 DsmData.Row resource = data.get(row);
156 for (int col = 0; col < resource.size(); col++) {
157 Label cell = createGridCell(row, col, resource.getWeight(col));
158 grid.setWidget(row, col, cell);
159 cells[row][col] = cell;
160 }
161 }
162 return grid;
163 }
164
165
166 /* ---------------- ACTIONS -------------------- */
167
168 public void onCellClicked(int row, int col) {
169 cancelHighlighting();
170
171 highlightTitle(row);
172 highlightTitle(col);
173 highlightIndicator(row);
174 highlightIndicator(col);
175
176 for (int i = 0; i < cells.length; i++) {
177 for (int j = 0; j < cells.length; j++) {
178 Label cell = cells[i][j];
179 if (i == row && j == col) {
180 highlightCell(cell, GRID_CELL_SELECTION1_SUFFIX);
181
182 } else if (j == row && i == col) {
183 // opposite
184 highlightCell(cell, GRID_CELL_SELECTION1_SUFFIX);
185
186 } else if (j == col || i == col) {
187 highlightCell(cell, GRID_CELL_COMB1_SUFFIX);
188
189 } else if (i == row || j == row) {
190 highlightCell(cell, GRID_CELL_COMB2_SUFFIX);
191 }
192 }
193 }
194 }
195
196 private void displayDependencyInfo(int row, int col) {
197 DsmData.Cell cell = data.get(row).getCell(col);
198 DependencyInfo.getInstance().showOrPopup(cell.getDependencyId());
199 }
200
201 public void onTitleClicked(int row) {
202 cancelHighlighting();
203 highlightTitle(row);
204 highlightIndicator(row);
205
206 // highlight row
207 for (int col = 0; col < cells[row].length; col++) {
208 highlightCell(cells[row][col], GRID_CELL_SELECTION2_SUFFIX);
209 if (col < row && hasWeight(cells[row][col])) {
210 highlightIndicator(col, true);
211 }
212 }
213
214 // highlight column
215 for (int i = 0; i < cells.length; i++) {
216 if (i != row) {
217 highlightCell(cells[i][row], GRID_CELL_SELECTION2_SUFFIX);
218 if (i > row && hasWeight(cells[i][row])) {
219 highlightIndicator(i, false);
220 }
221 }
222 }
223 }
224
225 private boolean hasWeight(Label label) {
226 return label.getText().length() > 0;
227 }
228
229
230 /*--------- EFFECTS ----------*/
231 private void cancelHighlighting() {
232 cancelGridHighlighting();
233 cancelIndicatorsHighlighting();
234 cancelTitlesHighlighting();
235 }
236
237 private void cancelGridHighlighting() {
238 for (Label cell : highlightedCells) {
239 for (String suffix : GRID_SUFFIXES) {
240 cell.removeStyleDependentName(suffix);
241 }
242 }
243 highlightedCells.clear();
244 }
245
246 private void highlightCell(Label cell, String style) {
247 cell.addStyleDependentName(style);
248 highlightedCells.add(cell);
249 }
250
251 private void highlightTitle(int row) {
252 titles[row].addStyleDependentName(HEADER_SELECTED_SUFFIX);
253 }
254
255 private void cancelTitlesHighlighting() {
256 for (Label title : titles) {
257 title.removeStyleDependentName(HEADER_SELECTED_SUFFIX);
258 }
259 }
260
261 private void cancelIndicatorsHighlighting() {
262 for (Label indicator : indicators) {
263 indicator.removeStyleDependentName(HEADER_HIGHER_INDICATOR_SUFFIX);
264 indicator.removeStyleDependentName(HEADER_LOWER_INDICATOR_SUFFIX);
265 indicator.removeStyleDependentName(HEADER_SELECTED_SUFFIX);
266 }
267 }
268
269 private void highlightIndicator(int row) {
270 indicators[row].addStyleDependentName(HEADER_SELECTED_SUFFIX);
271 }
272
273 private void highlightIndicator(int row, boolean higher) {
274 indicators[row].addStyleDependentName(higher ? HEADER_HIGHER_INDICATOR_SUFFIX : HEADER_LOWER_INDICATOR_SUFFIX);
275 }
276
277
278 /* ---------- COMPONENTS ------------ */
279 private Label createGridCell(final int row, final int col, final int weight) {
280 Label cell;
281 if (row == col) {
282 cell = createDiagonalCell(row);
283
284 } else {
285 cell = createNonDiagonalCell(row, col, weight);
286 }
287 return cell;
288 }
289
290 private Label createNonDiagonalCell(final int row, final int col, int weight) {
291 Label cell;
292 cell = buildCell(row, col, weight, (col > row ? GRID_CELL_TOP_RIGHT : GRID_CELL_BOTTOM_LEFT));
293
294 if (weight > 0) {
295 String tooltip = data.get(col).getName() + " -> " + data.get(row).getName() + " (" + weight + "). " + Dictionary.getDictionary("l10n").get("design.cellTooltip");
296 cell.setTitle(tooltip);
297 }
298 return cell;
299 }
300
301 private Label createDiagonalCell(final int row) {
302 Label cell;
303 cell = buildLabel("-", GRID_CELL_DIAGONAL);
304 cell.addClickHandler(new ClickHandler() {
305 public void onClick(final ClickEvent event) {
306 onTitleClicked(row);
307 }
308 });
309 return cell;
310 }
311
312 private HTML buildRowTitle(final int indexRow, final DsmData.Row row) {
313 HTML title = new HTML(Icons.forQualifier(row.getQualifier()).getHTML() + " " + row.getName()) {
314 {
315 addDomHandler(new DoubleClickHandler() {
316 public void onDoubleClick(DoubleClickEvent pEvent) {
317 if (row.getId() != null) {
318 if (!"FIL".equals(row.getQualifier()) && !"CLA".equals(row.getQualifier())) {
319 Window.Location.assign(Links.urlForResourcePage(row.getId(), DesignPage.GWT_ID, null));
320 } else {
321 Links.openMeasurePopup(row.getId(), null);
322 }
323 }
324 }
325 }, DoubleClickEvent.getType());
326 }
327 };
328 title.setStylePrimaryName(HEADER_TITLE);
329 title.setTitle(Dictionary.getDictionary("l10n").get("design.rowTooltip"));
330 final int finalIndexRow = indexRow;
331 title.addClickHandler(new ClickHandler() {
332 public void onClick(ClickEvent event) {
333 onTitleClicked(finalIndexRow);
334 }
335 });
336 return title;
337 }
338
339 private static Label buildLabel(String text, String primaryStyle) {
340 Label label = new Label(text);
341 label.setStylePrimaryName(primaryStyle);
342 return label;
343 }
344
345 private Label buildCell(final int row, final int col, int weight, String primaryStyle) {
346 String text = "";
347 if (weight > 0) {
348 text = "<span>" + Integer.toString(weight) + "</span>";
349 }
350
351 HTML cell = new HTML(text) {
352 {
353 addDomHandler(new DoubleClickHandler() {
354 public void onDoubleClick(DoubleClickEvent pEvent) {
355 displayDependencyInfo(row, col);
356 }
357 }, DoubleClickEvent.getType());
358 }
359 };
360 cell.addClickHandler(new ClickHandler() {
361 public void onClick(final ClickEvent event) {
362 onCellClicked(row, col);
363 }
364 });
365 cell.setStylePrimaryName(primaryStyle);
366 return cell;
367 }
368
369 }