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