View Javadoc
1   /*******************************************************************************
2    * jArduino: Arduino C++ Code Generation From Java
3    * Copyright 2020 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   ******************************************************************************/
17  package net.sourceforge.jarduino.gui;
18  
19  import java.awt.BorderLayout;
20  import java.awt.CardLayout;
21  import java.awt.Dimension;
22  import java.awt.Font;
23  import java.io.BufferedReader;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.io.InputStreamReader;
28  import java.io.PrintWriter;
29  import java.nio.charset.Charset;
30  import javax.swing.BorderFactory;
31  import javax.swing.Box;
32  import javax.swing.BoxLayout;
33  import javax.swing.JButton;
34  import javax.swing.JComponent;
35  import javax.swing.JFrame;
36  import javax.swing.JPanel;
37  import javax.swing.JScrollPane;
38  import javax.swing.JTextArea;
39  import javax.swing.border.Border;
40  
41  import net.sourceforge.jarduino.ArduinoException;
42  import net.sourceforge.jarduino.message.ArduinoChar;
43  import net.sourceforge.jarduino.message.ArduinoLibrary;
44  import net.sourceforge.jarduino.message.ArduinoGenerator;
45  import net.sourceforge.jarduino.message.ArduinoMsgFilter;
46  import net.sourceforge.jarduino.message.ArduinoParser;
47  import net.sourceforge.jarduino.message.ArduinoSystem;
48  
49  /**
50   * Panel for displaying sources.
51   */
52  public class ArduinoPanelSource {
53      /**
54       * Filter panel name.
55       */
56      private static final String FILTER = "Filter";
57  
58      /**
59       * Source panel name.
60       */
61      private static final String SOURCE = "Source";
62  
63      /**
64       * Font.
65       */
66      static final Font FONT = new Font("monospaced", Font.PLAIN, 12);
67  
68      /**
69       * The Panel.
70       */
71      private final JPanel thePanel;
72  
73      /**
74       * The FileSetButton.
75       */
76      private final ArduinoScrollButton<ArduinoFileSet> theFileSetButton;
77  
78      /**
79       * The FileButton.
80       */
81      private final ArduinoScrollButton<ArduinoFile> theFileButton;
82  
83      /**
84       * The textArea.
85       */
86      private final JTextArea theSourceFile;
87  
88      /**
89       * The filter panel.
90       */
91      private final ArduinoPanelFilter theFilterPanel;
92  
93      /**
94       * The current system.
95       */
96      private ArduinoSystem theSystem;
97  
98      /**
99       * The sourceDBC.
100      */
101     private String theDBCFile;
102 
103     /**
104      * The error.
105      */
106     private ArduinoException theError;
107 
108     /**
109      * Constructor.
110      * @param pFrame the frame
111      */
112     ArduinoPanelSource(final JFrame pFrame) {
113         /* Create the panel */
114         thePanel = new JPanel(new CardLayout());
115 
116         /* Create the filter panel */
117         theFilterPanel = new ArduinoPanelFilter(this, pFrame);
118 
119         /* Create the TextPane */
120         theSourceFile = new JTextArea();
121         theSourceFile.setEditable(false);
122         theSourceFile.setFont(FONT);
123 
124         /* Create the Filter button */
125         final JButton myFilterButton = new JButton();
126         myFilterButton.setText("Sources");
127         myFilterButton.addActionListener(e -> showFilter());
128 
129         /* Create the file button */
130         theFileButton = new ArduinoScrollButton<>(pFrame);
131         theFileButton.onSelect(this::showSourceFile);
132         theFileButton.setFormatter(e -> e.getFileName(theSystem));
133 
134         /* Create the fileSet button */
135         theFileSetButton = new ArduinoScrollButton<>(pFrame);
136         theFileSetButton.onSelect(this::populateFileSet);
137         for (ArduinoFileSet myFileSet : ArduinoFileSet.values()) {
138             theFileSetButton.add(myFileSet);
139         }
140 
141         /* Create the file panel */
142         final JPanel myFilePanel = new JPanel();
143         myFilePanel.setLayout(new BoxLayout(myFilePanel, BoxLayout.X_AXIS));
144         myFilePanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
145         myFilePanel.add(theFileSetButton.getComponent());
146         myFilePanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
147         myFilePanel.add(theFileButton.getComponent());
148         myFilePanel.add(Box.createHorizontalGlue());
149         Border myBorder = BorderFactory.createTitledBorder("Select File To View");
150         myFilePanel.setBorder(myBorder);
151 
152         /* Create the filter panel */
153         final JPanel myFilterPanel = new JPanel();
154         myFilterPanel.setLayout(new BoxLayout(myFilterPanel, BoxLayout.X_AXIS));
155         myFilterPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
156         myFilterPanel.add(myFilterButton);
157         myFilterPanel.add(Box.createHorizontalStrut(ArduinoPanelMain.STRUTSIZE));
158         myBorder = BorderFactory.createTitledBorder("View");
159         myFilterPanel.setBorder(myBorder);
160 
161         /* Create the control panel */
162         final JPanel myControl = new JPanel();
163         myControl.setLayout(new BoxLayout(myControl, BoxLayout.X_AXIS));
164         myControl.add(myFilePanel);
165         myControl.add(myFilterPanel);
166 
167         /* Create the fileView */
168         final JPanel myFileView = new JPanel(new BorderLayout());
169         myFileView.add(new JScrollPane(theSourceFile), BorderLayout.CENTER);
170         myBorder = BorderFactory.createTitledBorder(SOURCE);
171         myFileView.setBorder(myBorder);
172 
173         /* Create the main Panel */
174         final JPanel mySource = new JPanel(new BorderLayout());
175         mySource.add(myControl, BorderLayout.PAGE_START);
176         mySource.add(myFileView, BorderLayout.CENTER);
177 
178         /* Build the panel */
179         thePanel.add(mySource, SOURCE);
180         thePanel.add(theFilterPanel.getComponent(), FILTER);
181 
182         /* Set Dimensions */
183         thePanel.setPreferredSize(new Dimension(ArduinoPanelMain.WIDTH, ArduinoPanelMain.HEIGHT));
184     }
185 
186     /**
187      * Obtain the component.
188      * @return the component
189      */
190     JComponent getComponent() {
191         return thePanel;
192     }
193 
194     /**
195      * Obtain the system.
196      * @return the system
197      */
198     ArduinoSystem getSystem() {
199         return theSystem;
200     }
201 
202     /**
203      * Obtain the error.
204      * @return the error
205      */
206     ArduinoException getError() {
207         return theError;
208     }
209 
210     /**
211      * Show Filter Panel.
212      */
213     private void showFilter() {
214         ((CardLayout) thePanel.getLayout()).show(thePanel, FILTER);
215     }
216 
217     /**
218      * Show Source Panel.
219      */
220     void showSource() {
221         ((CardLayout) thePanel.getLayout()).show(thePanel, SOURCE);
222     }
223 
224     /**
225      * Update the filter.
226      */
227     void updateFilter() {
228         showSourceFile(theFileButton.getSelectedItem());
229     }
230 
231     /**
232      * Set selected.
233      * @param pSource the selected file
234      * @param pCharSet the character set to use
235      * @return success true/false
236      */
237     boolean setSelected(final File pSource,
238                         final Charset pCharSet) {
239         /* Protect against exceptions */
240         try {
241             /* Clear the error */
242             theError = null;
243 
244             /* Load the file */
245             final ArduinoSystem mySystem = ArduinoParser.parseFile(pSource, pCharSet);
246 
247             /* Load the source file */
248             theDBCFile = loadSource(pSource, pCharSet);
249 
250             /* Record the successful system and update the filter */
251             theSystem = mySystem;
252             theFilterPanel.setSystem(mySystem);
253             theFileSetButton.setSelectedItem(ArduinoFileSet.SRCDBC);
254             return true;
255 
256             /* Handle exceptions */
257         } catch (ArduinoException e) {
258             /* Record the error and return */
259             theError = e;
260             return false;
261         }
262     }
263 
264     /**
265      * Set System.
266      * @param pFile the source file
267      */
268     private void showSourceFile(final ArduinoFile pFile) {
269         /* Set Base Headers */
270         theSourceFile.setText(getSourceText(pFile));
271         theSourceFile.setCaretPosition(0);
272     }
273 
274     /**
275      * populate FileButton for fileSet.
276      * @param pFileSet the fileSet
277      */
278     private void populateFileSet(final ArduinoFileSet pFileSet) {
279         /* Set Base Headers */
280         theFileButton.removeAll();
281         for (ArduinoFile myFile : ArduinoFile.values()) {
282             if (myFile.isInFileSet(pFileSet)) {
283                 theFileButton.add(myFile);
284             }
285         }
286     }
287 
288     /**
289      * Obtain source text.
290      * @param pFile the source file
291      * @return the source text
292      */
293     private String getSourceText(final ArduinoFile pFile) {
294         /* Handle initialisation case */
295         if (theSystem == null) {
296             return null;
297         }
298 
299         /* Switch on file */
300         switch (pFile) {
301             case BASEHEADER:
302                 return ArduinoLibrary.loadLibraryHeader();
303             case BASEBODY:
304                 return ArduinoLibrary.loadLibraryCode();
305             case SRCHEADER:
306                 return ArduinoGenerator.formatHeader(theSystem, theFilterPanel.getFilter());
307             case SRCBODY:
308                 return ArduinoGenerator.formatBody(theSystem, theFilterPanel.getFilter());
309             case DBCFILE:
310             default:
311                 return theDBCFile;
312         }
313     }
314 
315     /**
316      * Load Source.
317      * @param pSource the source.
318      * @param pCharSet the character set to use
319      * @return the source
320      * @throws ArduinoException on error
321      */
322     private static String loadSource(final File pSource,
323                                      final Charset pCharSet) throws ArduinoException {
324         /* Protect against exceptions */
325         try (FileInputStream myStream = new FileInputStream(pSource);
326              InputStreamReader myReader = new InputStreamReader(myStream, pCharSet);
327              BufferedReader myInput = new BufferedReader(myReader)) {
328             /* Reset the builder */
329             final StringBuilder myBuilder = new StringBuilder();
330 
331             /* Read the header entry */
332             for (;;) {
333                 /* Read next line */
334                 final String myLine = myInput.readLine();
335                 if (myLine == null) {
336                     break;
337                 }
338 
339                 /* Add to the string buffer */
340                 myBuilder.append(myLine);
341                 myBuilder.append(ArduinoChar.LF);
342             }
343 
344             /* Build the string */
345             return myBuilder.toString();
346 
347             /* Catch exceptions */
348         } catch (IOException e) {
349             throw new ArduinoException("Failed to load File");
350         }
351     }
352 
353     /**
354      * Write Sketch Files to directory.
355      * @param pDirectory the directory to write to
356      * @param pCharSet the character set to use
357      * @return the exception (or null if successful)
358      */
359     ArduinoException writeSketchToDirectory(final File pDirectory,
360                                             final Charset pCharSet) {
361         /* Protect against exceptions */
362         try {
363             /* Obtain the message filter */
364             final ArduinoMsgFilter myFilter = theFilterPanel.getFilter();
365 
366             /* Write system header */
367             File myFile = new File(pDirectory, ArduinoFile.SRCHEADER.getFileName(theSystem));
368             writeStringToFile(ArduinoGenerator.formatHeader(theSystem, myFilter), pCharSet, myFile);
369 
370             /* Write system code */
371             myFile = new File(pDirectory, ArduinoFile.SRCBODY.getFileName(theSystem));
372             writeStringToFile(ArduinoGenerator.formatBody(theSystem, myFilter), pCharSet, myFile);
373 
374             /* Signal OK */
375             return null;
376 
377             /* Catch exceptions */
378         } catch (ArduinoException e) {
379             return e;
380         }
381     }
382 
383     /**
384      * Write Files to directory.
385      * @param pDirectory the directory to write to
386      * @param pCharSet the character set to use
387      * @return the exception (or null if successful)
388      */
389     ArduinoException writeLibraryToDirectory(final File pDirectory,
390                                              final Charset pCharSet) {
391         /* Protect against exceptions */
392         try {
393             /* Write base header */
394             File myFile = new File(pDirectory, ArduinoFile.BASEHEADER.getFileName(theSystem));
395             writeStringToFile(ArduinoLibrary.loadLibraryHeader(), pCharSet, myFile);
396 
397             /* Write base code */
398             myFile = new File(pDirectory, ArduinoFile.BASEBODY.getFileName(theSystem));
399             writeStringToFile(ArduinoLibrary.loadLibraryCode(), pCharSet, myFile);
400 
401             /* Signal OK */
402             return null;
403 
404             /* Catch exceptions */
405         } catch (ArduinoException e) {
406             return e;
407         }
408     }
409 
410     /**
411      * Write String to file.
412      * @param pData the String to write
413      * @param pCharSet the characterSet to use
414      * @param pFile the file to write to
415      * @throws ArduinoException on error
416      */
417     private static void writeStringToFile(final String pData,
418                                           final Charset pCharSet,
419                                           final File pFile) throws ArduinoException {
420         /* Protect the write */
421         try (PrintWriter myWriter = new PrintWriter(pFile, pCharSet.name())) {
422             /* Write the data to the file */
423             myWriter.print(pData);
424 
425         } catch (IOException e) {
426             throw new ArduinoException("Failed to output to file", pFile.getAbsolutePath());
427         }
428     }
429 
430     /**
431      * The source files.
432      */
433     private enum ArduinoFile {
434         /**
435          * Source Header.
436          */
437         SRCHEADER,
438 
439         /**
440          * Source Body.
441          */
442         SRCBODY,
443 
444         /**
445          * Base Header.
446          */
447         BASEHEADER,
448 
449         /**
450          * Base Body.
451          */
452         BASEBODY,
453 
454         /**
455          * DBCFile.
456          */
457         DBCFILE;
458 
459         /**
460          * Obtain name.
461          * @param pSystem the system
462          * @return the name
463          */
464         String getFileName(final ArduinoSystem pSystem) {
465             /* Handle initialisation */
466             if (pSystem == null) {
467                 return "Unknown";
468             }
469 
470             /* Switch on file */
471             switch (this) {
472                 case SRCHEADER:
473                     return pSystem.getCName() + ".h";
474                 case SRCBODY:
475                     return pSystem.getCName() + ".cpp";
476                 case BASEHEADER:
477                     return ArduinoLibrary.HEADER;
478                 case BASEBODY:
479                     return ArduinoLibrary.CODE;
480                 case DBCFILE:
481                 default:
482                     return pSystem.getName() + ".dbc";
483             }
484         }
485 
486         /**
487          * is this file in the fileSet?
488          * @param pFileSet the fileSet
489          * @return true/false
490          */
491         boolean isInFileSet(final ArduinoFileSet pFileSet) {
492             /* Switch on file */
493             switch (this) {
494                 case SRCBODY:
495                 case SRCHEADER:
496                     return ArduinoFileSet.SKETCH.equals(pFileSet);
497                 case BASEHEADER:
498                 case BASEBODY:
499                     return ArduinoFileSet.LIBRARY.equals(pFileSet);
500                 case DBCFILE:
501                 default:
502                     return ArduinoFileSet.SRCDBC.equals(pFileSet);
503             }
504         }
505     }
506 
507     /**
508      * The fileSet files.
509      */
510     private enum ArduinoFileSet {
511         /**
512          * Source DBC.
513          */
514         SRCDBC,
515 
516         /**
517          * Sketch.
518          */
519         SKETCH,
520 
521         /**
522          * Library.
523          */
524         LIBRARY;
525 
526         @Override
527         public String toString() {
528             switch (this) {
529                 case SRCDBC:
530                     return "Source DBC";
531                 case SKETCH:
532                     return "Sketch";
533                 case LIBRARY:
534                 default:
535                     return "Library";
536             }
537         }
538     }
539 }