001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2009 SonarSource SA 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.gwt.ui; 021 022 import com.google.gwt.event.dom.client.ClickEvent; 023 import com.google.gwt.event.dom.client.ClickHandler; 024 import com.google.gwt.user.client.ui.*; 025 import com.google.gwt.widgetideas.table.client.PreloadedTable; 026 import org.sonar.wsclient.gwt.AbstractCallback; 027 import org.sonar.wsclient.gwt.Sonar; 028 import org.sonar.wsclient.services.Resource; 029 import org.sonar.wsclient.services.Source; 030 import org.sonar.wsclient.services.SourceQuery; 031 032 import java.util.*; 033 034 public abstract class SourcePanel extends Composite implements ClickHandler { 035 036 private static final int MAX_LINES_BY_BLOCK = 3000; 037 038 private final Panel panel = new VerticalPanel(); 039 private PreloadedTable rowsTable; 040 private Source source; 041 private final Loading loading = new Loading(); 042 private int from = 0; 043 private int length = 0; 044 045 private boolean started = false; 046 private Resource resource; 047 private boolean hasNoSources = false; 048 049 // pagination 050 private Button moreButton = null; 051 private int currentRow = 0; 052 private int offset = 0; 053 private boolean firstPage = true; 054 private boolean previousLineIsDecorated = true; 055 private boolean firstDecoratedLine = true; 056 057 public SourcePanel(Resource resource) { 058 this(resource, 0, 0); 059 } 060 061 public Resource getResource() { 062 return resource; 063 } 064 065 public SourcePanel(Resource resource, int from, int length) { 066 this.from = from; 067 this.length = length; 068 this.resource = resource; 069 070 panel.add(loading); 071 panel.getElement().setId("sourcePanel"); 072 initWidget(panel); 073 setStyleName("gwt-SourcePanel"); 074 075 loadSources(); 076 } 077 078 public void onClick(ClickEvent clickEvent) { 079 if (clickEvent.getSource() == moreButton) { 080 hideMoreButton(); 081 displaySource(); 082 } 083 } 084 085 public Button getMoreButton() { 086 if (moreButton == null) { 087 moreButton = new Button("More"); 088 moreButton.getElement().setId("more_source"); 089 moreButton.addClickHandler(this); 090 } 091 return moreButton; 092 } 093 094 public void showMoreButton() { 095 hideMoreButton(); 096 panel.add(getMoreButton()); 097 } 098 099 public void hideMoreButton() { 100 getMoreButton().removeFromParent(); 101 } 102 103 104 public void refresh() { 105 if (!hasNoSources) { 106 panel.clear(); 107 panel.add(loading); 108 109 rowsTable = null; 110 currentRow = 0; 111 offset = 0; 112 firstPage = true; 113 previousLineIsDecorated = true; 114 firstDecoratedLine = true; 115 displaySource(); 116 } 117 } 118 119 private void loadSources() { 120 Sonar.getInstance().find(SourceQuery.create(resource.getId().toString()) 121 .setLinesFromLine(from, length) 122 .setHighlightedSyntax(true), new AbstractCallback<Source>(loading) { 123 124 @Override 125 protected void doOnResponse(Source result) { 126 source = result; 127 displaySource(); 128 } 129 130 @Override 131 protected void doOnError(int errorCode, String errorMessage) { 132 if (errorCode == 404) { 133 panel.add(new HTML("<p style=\"padding: 5px\">No sources</p>")); 134 hasNoSources = true; 135 loading.removeFromParent(); 136 137 } else if (errorCode == 401) { 138 panel.add(new HTML("<p style=\"padding: 5px\">You're not authorized to view source code</p>")); 139 hasNoSources = true; 140 loading.removeFromParent(); 141 142 } else { 143 super.onError(errorCode, errorMessage); 144 } 145 } 146 147 }); 148 } 149 150 protected void setStarted() { 151 started = true; 152 displaySource(); 153 } 154 155 private void displaySource() { 156 if (started && source != null) { 157 createRowsTable(); 158 159 int displayedLines = 0; 160 SortedMap<Integer, String> lines = source.getLinesById().subMap(offset, offset + source.getLinesById().lastKey() + 1); 161 Iterator<Map.Entry<Integer, String>> linesById = lines.entrySet().iterator(); 162 163 while (displayedLines < MAX_LINES_BY_BLOCK && linesById.hasNext()) { 164 Map.Entry<Integer, String> entry = linesById.next(); 165 Integer lineIndex = entry.getKey(); 166 if (shouldDecorateLine(lineIndex)) { 167 if (!previousLineIsDecorated && !firstDecoratedLine) { 168 setRowHtml(0, "<div class='src' style='background-color: #fff;height: 3em; border-top: 1px dashed silver;border-bottom: 1px dashed silver;'> </div>"); 169 setRowHtml(1, " "); 170 setRowHtml(2, " "); 171 setRowHtml(3, "<div class='src' style='background-color: #fff;height: 3em; border-top: 1px dashed silver;border-bottom: 1px dashed silver;'> </div>"); 172 currentRow++; 173 } 174 175 List<Row> rows = decorateLine(lineIndex, entry.getValue()); 176 if (rows != null) { 177 for (Row row : rows) { 178 setRowHtml(0, row.getColumn1()); 179 setRowHtml(1, row.getColumn2()); 180 setRowHtml(2, row.getColumn3()); 181 setRowHtml(3, row.getColumn4()); 182 currentRow++; 183 } 184 previousLineIsDecorated = true; 185 firstDecoratedLine = false; 186 } 187 displayedLines++; 188 189 } else { 190 previousLineIsDecorated = false; 191 } 192 offset++; 193 } 194 195 if (firstPage) { 196 panel.clear(); 197 panel.add(rowsTable); 198 firstPage = false; 199 } 200 201 if (offset <= source.getLinesById().lastKey()) { 202 showMoreButton(); 203 } else { 204 hideMoreButton(); 205 } 206 } 207 } 208 209 private void setRowHtml(int colNum, String html) { 210 if (firstPage) { 211 rowsTable.setPendingHTML(currentRow, colNum, html); 212 } else { 213 rowsTable.setHTML(currentRow, colNum, html); 214 } 215 } 216 217 private void createRowsTable() { 218 if (rowsTable == null) { 219 rowsTable = new PreloadedTable(); 220 rowsTable.setStyleName("sources code"); 221 222 offset = source.getLinesById().firstKey(); 223 if (shouldDecorateLine(0)) { 224 List<Row> rows = decorateLine(0, null); 225 if (rows != null) { 226 for (Row row : rows) { 227 rowsTable.setPendingHTML(currentRow, 0, row.getColumn1()); 228 rowsTable.setPendingHTML(currentRow, 1, row.getColumn2()); 229 rowsTable.setPendingHTML(currentRow, 2, row.getColumn3()); 230 rowsTable.setPendingHTML(currentRow, 3, row.getColumn4()); 231 currentRow++; 232 } 233 } 234 } 235 } 236 } 237 238 protected boolean shouldDecorateLine(int index) { 239 return true; 240 } 241 242 protected List<Row> decorateLine(int index, String source) { 243 if (index > 0) { 244 return Arrays.asList(new Row(index, source)); 245 } 246 return null; 247 } 248 249 public static class Row { 250 protected String column1; 251 protected String column2; 252 protected String column3; 253 protected String column4; 254 255 public Row(String column1, String column2, String column3) { 256 this.column1 = column1; 257 this.column2 = column2; 258 this.column3 = column3; 259 this.column4 = ""; 260 } 261 262 public Row(String column1, String column2, String column3, String column4) { 263 this.column1 = column1; 264 this.column2 = column2; 265 this.column3 = column3; 266 this.column4 = column4; 267 } 268 269 public Row(int lineIndex, String source) { 270 setLineIndex(lineIndex, ""); 271 unsetValue(); 272 setSource(source, ""); 273 } 274 275 public Row() { 276 } 277 278 public Row setLineIndex(int index, String style) { 279 column1 = "<div class='ln " + style + "'>" + index + "</div>"; 280 return this; 281 } 282 283 public Row setValue(String value, String style) { 284 column2 = "<div class='val " + style + "'>" + value + "</div>"; 285 return this; 286 } 287 288 public Row setValue2(String value, String style) { 289 column3 = "<div class='val " + style + "'>" + value + "</div>"; 290 return this; 291 } 292 293 public Row unsetValue() { 294 column2 = ""; 295 column3 = ""; 296 return this; 297 } 298 299 public Row setSource(String source, String style) { 300 column4 = "<div class='src " + style + "'><pre>" + source + "</pre></div>"; 301 return this; 302 } 303 304 public String getColumn1() { 305 return column1; 306 } 307 308 public void setColumn1(String column1) { 309 this.column1 = column1; 310 } 311 312 public String getColumn2() { 313 return column2; 314 } 315 316 public void setColumn2(String column2) { 317 this.column2 = column2; 318 } 319 320 public String getColumn3() { 321 return column3; 322 } 323 324 public void setColumn3(String column3) { 325 this.column3 = column3; 326 } 327 328 public String getColumn4() { 329 return column4; 330 } 331 332 public void setColumn4(String column4) { 333 this.column4 = column4; 334 } 335 } 336 }