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.widgetideas.table.client.PreloadedTable;
023    import com.google.gwt.user.client.ui.Composite;
024    import com.google.gwt.user.client.ui.HTML;
025    import com.google.gwt.user.client.ui.Panel;
026    import com.google.gwt.user.client.ui.VerticalPanel;
027    import org.sonar.wsclient.gwt.AbstractCallback;
028    import org.sonar.wsclient.gwt.Sonar;
029    import org.sonar.wsclient.services.Resource;
030    import org.sonar.wsclient.services.Source;
031    import org.sonar.wsclient.services.SourceQuery;
032    
033    import java.util.Arrays;
034    import java.util.List;
035    import java.util.Map;
036    import java.util.SortedMap;
037    
038    public abstract class SourcePanel extends Composite {
039    
040      private final Panel panel = new VerticalPanel();
041      private Source source;
042      private final Loading loading = new Loading();
043      private int from = 0;
044      private int length = 0;
045    
046      private boolean started = false;
047      private Resource resource;
048      private boolean hasNoSources = false;
049    
050      public SourcePanel(Resource resource) {
051        this(resource, 0, 0);
052      }
053    
054      public Resource getResource() {
055        return resource;
056      }
057    
058      public SourcePanel(Resource resource, int from, int length) {
059        this.from = from;
060        this.length = length;
061        this.resource = resource;
062    
063        panel.add(loading);
064        panel.getElement().setId("sourcePanel");
065        initWidget(panel);
066        setStyleName("gwt-SourcePanel");
067    
068        loadSources();
069      }
070    
071      private void loadSources() {
072        Sonar.getInstance().find(SourceQuery.create(resource.getId().toString())
073            .setLinesFromLine(from, length)
074            .setHighlightedSyntax(true), new AbstractCallback<Source>(loading) {
075    
076          @Override
077          protected void doOnResponse(Source result) {
078            source = result;
079            decorate();
080          }
081    
082          @Override
083          protected void doOnError(int errorCode, String errorMessage) {
084            if (errorCode == 404) {
085              panel.add(new HTML("<p style=\"padding: 5px\">No sources</p>"));
086              hasNoSources = true;
087              loading.removeFromParent();
088    
089            } else if (errorCode == 401) {
090              panel.add(new HTML("<p style=\"padding: 5px\">You're not authorized to view source code</p>"));
091              hasNoSources = true;
092              loading.removeFromParent();
093    
094            } else {
095              super.onError(errorCode, errorMessage);
096            }
097          }
098    
099        });
100      }
101    
102      protected void setStarted() {
103        started = true;
104        decorate();
105      }
106    
107      public void refresh() {
108        if (!hasNoSources) {
109          panel.clear();
110          panel.add(loading);
111          decorate();
112        }
113      }
114    
115      private void decorate() {
116        if (started && source != null) {
117          PreloadedTable table = new PreloadedTable();
118          table.setStyleName("sources code");
119    
120          int rowIndex = 0;
121          if (shouldDecorateLine(0)) {
122            List<Row> rows = decorateLine(0, null);
123            if (rows != null) {
124              for (Row row : rows) {
125                table.setPendingHTML(rowIndex, 0, row.getColumn1());
126                table.setPendingHTML(rowIndex, 1, row.getColumn2());
127                table.setPendingHTML(rowIndex, 2, row.getColumn3());
128                table.setPendingHTML(rowIndex, 3, row.getColumn4());
129                rowIndex++;
130              }
131            }
132          }
133    
134          SortedMap<Integer, String> lines = source.getLinesById();
135          boolean previousLineIsDecorated = true;
136          boolean firstDecoratedLine = true;
137          for (Map.Entry<Integer, String> entry : lines.entrySet()) {
138            Integer lineIndex = entry.getKey();
139            if (shouldDecorateLine(lineIndex)) {
140              if (!previousLineIsDecorated && !firstDecoratedLine) {
141                table.setPendingHTML(rowIndex, 0, "<div class='src' style='background-color: #fff;height: 3em; border-top: 1px dashed silver;border-bottom: 1px dashed silver;'> </div>");
142                table.setPendingHTML(rowIndex, 1, " ");
143                table.setPendingHTML(rowIndex, 2, " ");
144                table.setPendingHTML(rowIndex, 3, "<div class='src' style='background-color: #fff;height: 3em; border-top: 1px dashed silver;border-bottom: 1px dashed silver;'> </div>");
145                rowIndex++;
146              }
147    
148              List<Row> rows = decorateLine(lineIndex, entry.getValue());
149              if (rows != null) {
150                for (Row row : rows) {
151                  table.setPendingHTML(rowIndex, 0, row.getColumn1());
152                  table.setPendingHTML(rowIndex, 1, row.getColumn2());
153                  table.setPendingHTML(rowIndex, 2, row.getColumn3());
154                  table.setPendingHTML(rowIndex, 3, row.getColumn4());
155                  rowIndex++;
156                }
157                previousLineIsDecorated = true;
158                firstDecoratedLine = false;
159              }
160    
161            } else {
162              previousLineIsDecorated = false;
163            }
164          }
165          panel.clear();
166          panel.add(table);
167        }
168      }
169    
170      protected boolean shouldDecorateLine(int index) {
171        return true;
172      }
173    
174      protected List<Row> decorateLine(int index, String source) {
175        if (index > 0) {
176          return Arrays.asList(new Row(index, source));
177        }
178        return null;
179      }
180    
181      public static class Row {
182        protected String column1;
183        protected String column2;
184        protected String column3;
185        protected String column4;
186    
187        public Row(String column1, String column2, String column3) {
188          this.column1 = column1;
189          this.column2 = column2;
190          this.column3 = column3;
191          this.column4 = "";
192        }
193    
194        public Row(String column1, String column2, String column3, String column4) {
195          this.column1 = column1;
196          this.column2 = column2;
197          this.column3 = column3;
198          this.column4 = column4;
199        }
200    
201        public Row(int lineIndex, String source) {
202          setLineIndex(lineIndex, "");
203          unsetValue();
204          setSource(source, "");
205        }
206    
207        public Row() {
208        }
209    
210        public Row setLineIndex(int index, String style) {
211          column1 = "<div class='ln " + style + "'>" + index + "</div>";
212          return this;
213        }
214    
215        public Row setValue(String value, String style) {
216          column2 = "<div class='val " + style + "'>" + value + "</div>";
217          return this;
218        }
219    
220        public Row setValue2(String value, String style) {
221          column3 = "<div class='val " + style + "'>" + value + "</div>";
222          return this;
223        }
224    
225        public Row unsetValue() {
226          column2 = "";
227          column3 = "";
228          return this;
229        }
230    
231        public Row setSource(String source, String style) {
232          column4 = "<div class='src " + style + "'><pre>" + source + "</pre></div>";
233          return this;
234        }
235    
236        public String getColumn1() {
237          return column1;
238        }
239    
240        public void setColumn1(String column1) {
241          this.column1 = column1;
242        }
243    
244        public String getColumn2() {
245          return column2;
246        }
247    
248        public void setColumn2(String column2) {
249          this.column2 = column2;
250        }
251    
252        public String getColumn3() {
253          return column3;
254        }
255    
256        public void setColumn3(String column3) {
257          this.column3 = column3;
258        }
259    
260        public String getColumn4() {
261          return column4;
262        }
263    
264        public void setColumn4(String column4) {
265          this.column4 = column4;
266        }
267      }
268    }