View Javadoc
1   /*
2    * Copyright (c) 2020 bahlef.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v2.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v20.html
7    * Contributors:
8    * bahlef - initial API and implementation and/or initial documentation
9    */
10  
11  package de.funfried.netbeans.plugins.external.formatter.java.eclipse.ui;
12  
13  import java.awt.Color;
14  import java.awt.event.ActionEvent;
15  import java.awt.event.ActionListener;
16  import java.awt.event.FocusAdapter;
17  import java.awt.event.FocusEvent;
18  import java.awt.event.ItemEvent;
19  import java.awt.event.ItemListener;
20  import java.io.File;
21  import java.io.IOException;
22  import java.net.URL;
23  import java.nio.file.Path;
24  import java.nio.file.Paths;
25  import java.util.List;
26  import java.util.Objects;
27  import java.util.logging.Level;
28  import java.util.logging.Logger;
29  import java.util.prefs.Preferences;
30  
31  import javax.swing.DefaultComboBoxModel;
32  import javax.swing.GroupLayout;
33  import javax.swing.JButton;
34  import javax.swing.JCheckBox;
35  import javax.swing.JComboBox;
36  import javax.swing.JLabel;
37  import javax.swing.JTextField;
38  import javax.swing.LayoutStyle;
39  import javax.swing.SwingConstants;
40  import javax.swing.event.DocumentEvent;
41  import javax.swing.event.DocumentListener;
42  import javax.swing.filechooser.FileFilter;
43  import javax.swing.filechooser.FileNameExtensionFilter;
44  
45  import org.apache.commons.lang3.StringUtils;
46  import org.netbeans.api.project.Project;
47  import org.openide.awt.Mnemonics;
48  import org.openide.filesystems.FileChooserBuilder;
49  import org.openide.util.NbBundle;
50  import org.xml.sax.SAXException;
51  
52  import de.funfried.netbeans.plugins.external.formatter.eclipse.xml.ConfigReader;
53  import de.funfried.netbeans.plugins.external.formatter.exceptions.ConfigReadException;
54  import de.funfried.netbeans.plugins.external.formatter.java.eclipse.EclipseJavaFormatterSettings;
55  import de.funfried.netbeans.plugins.external.formatter.ui.options.AbstractFormatterOptionsPanel;
56  
57  /**
58   *
59   * @author bahlef
60   */
61  public class EclipseJavaFormatterOptionsPanel extends AbstractFormatterOptionsPanel {
62  	/** {@link Logger} of this class. */
63  	private static final Logger log = Logger.getLogger(EclipseJavaFormatterOptionsPanel.class.getName());
64  
65  	/**
66  	 * Creates new form {@link EclipseJavaFormatterOptionsPanel}.
67  	 *
68  	 * @param project the {@link Project} if the panel is used to modify project
69  	 *        specific settings, otherwise {@code null}
70  	 */
71  	public EclipseJavaFormatterOptionsPanel(Project project) {
72  		super(project);
73  
74  		initComponents();
75  
76  		if (project != null) {
77  			lblFormatterFile.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblFormatterFile.toolTipText.projectSpecific"));
78  			browseButton.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.browseButton.toolTipText.projectSpecific"));
79  		}
80  	}
81  
82  	/**
83  	 * This method is called from within the constructor to
84  	 * initialize the form.
85  	 * WARNING: Do NOT modify this code. The content of this method is
86  	 * always regenerated by the Form Editor.
87  	 */
88  	@SuppressWarnings("unchecked")
89      // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
90      private void initComponents() {
91  
92          lblFormatterFile = new JLabel();
93          formatterLocField = new JTextField();
94          browseButton = new JButton();
95          errorLabel = new JLabel();
96          jLabel2 = new JLabel();
97          cbProfile = new JComboBox<>();
98          lblProfile = new JLabel();
99          cbUseProjectPref = new JCheckBox();
100         lblLinefeed = new JLabel();
101         cbLinefeed = new JComboBox<>();
102         lblSourceLevel = new JLabel();
103         cbSourceLevel = new JComboBox<>();
104 
105         lblFormatterFile.setHorizontalAlignment(SwingConstants.RIGHT);
106         Mnemonics.setLocalizedText(lblFormatterFile, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblFormatterFile.text")); // NOI18N
107         lblFormatterFile.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblFormatterFile.toolTipText")); // NOI18N
108 
109         formatterLocField.setText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.formatterLocField.text")); // NOI18N
110         formatterLocField.setPreferredSize(formatterLocField.getMinimumSize());
111         formatterLocField.addFocusListener(new FocusAdapter() {
112             public void focusLost(FocusEvent evt) {
113                 formatterLocFieldFocusLost(evt);
114             }
115         });
116         formatterLocField.addActionListener(new ActionListener() {
117             public void actionPerformed(ActionEvent evt) {
118                 formatterLocFieldActionPerformed(evt);
119             }
120         });
121 
122         Mnemonics.setLocalizedText(browseButton, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.browseButton.text")); // NOI18N
123         browseButton.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.browseButton.toolTipText")); // NOI18N
124         browseButton.addActionListener(new ActionListener() {
125             public void actionPerformed(ActionEvent evt) {
126                 browseButtonActionPerformed(evt);
127             }
128         });
129 
130         errorLabel.setForeground(new Color(255, 51, 51));
131         Mnemonics.setLocalizedText(errorLabel, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.errorLabel.text")); // NOI18N
132 
133         Mnemonics.setLocalizedText(jLabel2, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.jLabel2.text")); // NOI18N
134         jLabel2.setEnabled(false);
135 
136         cbProfile.setEnabled(false);
137         cbProfile.addItemListener(new ItemListener() {
138             public void itemStateChanged(ItemEvent evt) {
139                 cbProfileItemStateChanged(evt);
140             }
141         });
142 
143         lblProfile.setHorizontalAlignment(SwingConstants.RIGHT);
144         Mnemonics.setLocalizedText(lblProfile, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblProfile.text")); // NOI18N
145         lblProfile.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblProfile.toolTipText")); // NOI18N
146 
147         cbUseProjectPref.setSelected(true);
148         Mnemonics.setLocalizedText(cbUseProjectPref, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.cbUseProjectPref.text")); // NOI18N
149         cbUseProjectPref.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.cbUseProjectPref.toolTipText")); // NOI18N
150 
151         lblLinefeed.setHorizontalAlignment(SwingConstants.RIGHT);
152         Mnemonics.setLocalizedText(lblLinefeed, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblLinefeed.text")); // NOI18N
153         lblLinefeed.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblLinefeed.toolTipText")); // NOI18N
154 
155         cbLinefeed.setModel(new DefaultComboBoxModel<>(new String[] { "System", "\\n", "\\r\\n", "\\r" }));
156         cbLinefeed.addItemListener(new ItemListener() {
157             public void itemStateChanged(ItemEvent evt) {
158                 cbLinefeedItemStateChanged(evt);
159             }
160         });
161 
162         Mnemonics.setLocalizedText(lblSourceLevel, NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblSourceLevel.text")); // NOI18N
163         lblSourceLevel.setToolTipText(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.lblSourceLevel.toolTipText")); // NOI18N
164 
165         cbSourceLevel.setModel(new DefaultComboBoxModel<>(new String[] { "No override", "1.9", "1.8", "1.7", "1.6", "1.5", "1.4" }));
166         cbSourceLevel.addItemListener(new ItemListener() {
167             public void itemStateChanged(ItemEvent evt) {
168                 cbSourceLevelItemStateChanged(evt);
169             }
170         });
171 
172         GroupLayout layout = new GroupLayout(this);
173         this.setLayout(layout);
174         layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
175             .addGroup(layout.createSequentialGroup()
176                 .addContainerGap()
177                 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING)
178                     .addComponent(lblFormatterFile)
179                     .addComponent(lblProfile, GroupLayout.PREFERRED_SIZE, 110, GroupLayout.PREFERRED_SIZE)
180                     .addComponent(lblLinefeed, GroupLayout.PREFERRED_SIZE, 110, GroupLayout.PREFERRED_SIZE))
181                 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
182                 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
183                     .addGroup(layout.createSequentialGroup()
184                         .addComponent(jLabel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
185                         .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
186                         .addComponent(errorLabel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
187                     .addGroup(layout.createSequentialGroup()
188                         .addComponent(formatterLocField, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
189                         .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
190                         .addComponent(browseButton))
191                     .addGroup(layout.createSequentialGroup()
192                         .addComponent(cbLinefeed, GroupLayout.PREFERRED_SIZE, 121, GroupLayout.PREFERRED_SIZE)
193                         .addGap(18, 18, 18)
194                         .addComponent(lblSourceLevel)
195                         .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
196                         .addComponent(cbSourceLevel, GroupLayout.PREFERRED_SIZE, 121, GroupLayout.PREFERRED_SIZE))
197                     .addComponent(cbUseProjectPref)
198                     .addComponent(cbProfile, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE))
199                 .addContainerGap())
200         );
201         layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
202             .addGroup(layout.createSequentialGroup()
203                 .addContainerGap()
204                 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
205                     .addComponent(lblFormatterFile)
206                     .addComponent(formatterLocField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
207                     .addComponent(browseButton))
208                 .addGap(7, 7, 7)
209                 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
210                     .addComponent(errorLabel, GroupLayout.PREFERRED_SIZE, 26, GroupLayout.PREFERRED_SIZE)
211                     .addComponent(jLabel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
212                 .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
213                 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
214                     .addComponent(cbProfile, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
215                     .addComponent(lblProfile))
216                 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
217                 .addComponent(cbUseProjectPref)
218                 .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
219                 .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
220                     .addComponent(cbLinefeed, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
221                     .addComponent(lblLinefeed)
222                     .addComponent(lblSourceLevel)
223                     .addComponent(cbSourceLevel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE))
224                 .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
225         );
226 
227         formatterLocField.getDocument().addDocumentListener(new DocumentListener() {
228             /**
229             * {@inheritDoc}
230             */
231             @Override
232             public void insertUpdate(DocumentEvent e) {
233                 fireChangedListener();
234             }
235 
236             /**
237             * {@inheritDoc}
238             */
239             @Override
240             public void removeUpdate(DocumentEvent e) {
241                 fireChangedListener();
242             }
243 
244             /**
245             * {@inheritDoc}
246             */
247             @Override
248             public void changedUpdate(DocumentEvent e) {
249                 fireChangedListener();
250             }
251         });
252     }// </editor-fold>//GEN-END:initComponents
253 
254     private void formatterLocFieldActionPerformed(ActionEvent evt) {//GEN-FIRST:event_formatterLocFieldActionPerformed
255 		loadEclipseFormatterFileForPreview(formatterLocField.getText(), getSelectedProfile());
256 		fireChangedListener();
257     }//GEN-LAST:event_formatterLocFieldActionPerformed
258 
259     private void browseButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed
260 		//The default dir to use if no value is stored
261 		File home = new File(System.getProperty("user.home"));
262 		final FileNameExtensionFilter fileNameExtensionFilterXML = new FileNameExtensionFilter("Eclipse formatter (*.xml)", "xml");
263 		final FileNameExtensionFilter fileNameExtensionFilterEPF = new FileNameExtensionFilter("Workspace mechanic (*.epf)", "epf");
264 		final FileFilter fileNameExtensionFilterProjectSetting = new FileFilter() {
265 			/**
266 			 * {@inheritDoc}
267 			 */
268 			@Override
269 			public boolean accept(File f) {
270 				if (f.isDirectory()) {
271 					return true;
272 				}
273 				return EclipseJavaFormatterSettings.PROJECT_PREF_FILE.equals(f.getName());
274 			}
275 
276 			/**
277 			 * {@inheritDoc}
278 			 */
279 			@Override
280 			public String getDescription() {
281 				return "Eclipse project settings (" + EclipseJavaFormatterSettings.PROJECT_PREF_FILE + ")";
282 			}
283 		};
284 		//Now build a file chooser and invoke the dialog in one line of code
285 		//"user-dir" is our unique key
286 		File toAdd = new FileChooserBuilder("user-dir").setFileHiding(false).setFilesOnly(true).setTitle("Choose configuration ...").setDefaultWorkingDirectory(home).setApproveText("Choose")
287 		  .addFileFilter(fileNameExtensionFilterProjectSetting).addFileFilter(fileNameExtensionFilterXML).addFileFilter(fileNameExtensionFilterEPF).setFileFilter(fileNameExtensionFilterXML)
288 		  .showOpenDialog();
289 		//Result will be null if the user clicked cancel or closed the dialog w/o OK
290 		if (toAdd != null) {
291 			loadEclipseFormatterFileForPreview(toAdd.getAbsolutePath(), getSelectedProfile());
292 			fireChangedListener();
293 		}
294     }//GEN-LAST:event_browseButtonActionPerformed
295 
296     private void cbProfileItemStateChanged(ItemEvent evt) {//GEN-FIRST:event_cbProfileItemStateChanged
297 		if (ItemEvent.SELECTED == evt.getStateChange()) {
298 			cbProfile.setToolTipText(Objects.toString(cbProfile.getSelectedItem(), null));
299 
300 			fireChangedListener();
301 		}
302     }//GEN-LAST:event_cbProfileItemStateChanged
303 
304     private void cbLinefeedItemStateChanged(ItemEvent evt) {//GEN-FIRST:event_cbLinefeedItemStateChanged
305 		if (ItemEvent.SELECTED == evt.getStateChange()) {
306 			cbLinefeed.setToolTipText(Objects.toString(cbLinefeed.getSelectedItem(), null));
307 
308 			fireChangedListener();
309 		}
310     }//GEN-LAST:event_cbLinefeedItemStateChanged
311 
312     private void cbSourceLevelItemStateChanged(ItemEvent evt) {//GEN-FIRST:event_cbSourceLevelItemStateChanged
313 		if (ItemEvent.SELECTED == evt.getStateChange()) {
314 			cbSourceLevel.setToolTipText(Objects.toString(cbSourceLevel.getSelectedItem(), null));
315 
316 			fireChangedListener();
317 		}
318     }//GEN-LAST:event_cbSourceLevelItemStateChanged
319 
320     private void formatterLocFieldFocusLost(FocusEvent evt) {//GEN-FIRST:event_formatterLocFieldFocusLost
321 		loadEclipseFormatterFileForPreview(formatterLocField.getText(), getSelectedProfile());
322 
323 		fireChangedListener();
324     }//GEN-LAST:event_formatterLocFieldFocusLost
325 
326     // Variables declaration - do not modify//GEN-BEGIN:variables
327     private JButton browseButton;
328     private JComboBox<String> cbLinefeed;
329     private JComboBox<String> cbProfile;
330     private JComboBox<String> cbSourceLevel;
331     private JCheckBox cbUseProjectPref;
332     private JLabel errorLabel;
333     private JTextField formatterLocField;
334     private JLabel jLabel2;
335     private JLabel lblFormatterFile;
336     private JLabel lblLinefeed;
337     private JLabel lblProfile;
338     private JLabel lblSourceLevel;
339     // End of variables declaration//GEN-END:variables
340 
341 		private String getLinefeed() {
342 			if (0 == cbLinefeed.getSelectedIndex()) {
343 				return "";
344 			}
345 			return cbLinefeed.getSelectedItem().toString();
346 		}
347 
348 		/**
349 		 * Returns the selected Eclipse formatter profile.
350 		 *
351 		 * @return the selected Eclipse formatter profile
352 		 */
353 		private String getSelectedProfile() {
354 			if (null != cbProfile.getSelectedItem()) {
355 				return cbProfile.getSelectedItem().toString();
356 			} else {
357 				return "";
358 			}
359 		}
360 
361 		private String getSourceLevel() {
362 			if (cbSourceLevel.getSelectedIndex() >= 1) {
363 				return "" + cbSourceLevel.getSelectedItem();
364 			} else {
365 				return "";
366 			}
367 		}
368 
369 		/**
370 		 * {@inheritDoc}
371 		 */
372 		@Override
373 		public void load(Preferences preferences) {
374 			String eclipseFormatterLocation = preferences.get(EclipseJavaFormatterSettings.CONFIG_FILE_LOCATION, "");
375 			String eclipseFormatterProfile = preferences.get(EclipseJavaFormatterSettings.ACTIVE_PROFILE, "");
376 			boolean useProjectPrefs = preferences.getBoolean(EclipseJavaFormatterSettings.USE_PROJECT_PREFS, true);
377 			String eclipseLineFeed = preferences.get(EclipseJavaFormatterSettings.LINEFEED, "");
378 			String sourceLevel = preferences.get(EclipseJavaFormatterSettings.SOURCELEVEL, "");
379 
380 			loadEclipseFormatterFileForPreview(eclipseFormatterLocation, eclipseFormatterProfile);
381 
382 			cbUseProjectPref.setSelected(useProjectPrefs);
383 
384 			if (StringUtils.isBlank(eclipseLineFeed)) {
385 				//default = system-dependend LF
386 				cbLinefeed.setSelectedIndex(0);
387 			} else {
388 				cbLinefeed.setSelectedItem(eclipseLineFeed);
389 			}
390 
391 			cbLinefeed.setToolTipText(Objects.toString(cbLinefeed.getSelectedItem(), null));
392 
393 			if (StringUtils.isBlank(sourceLevel)) {
394 				//default = No override
395 				cbSourceLevel.setSelectedIndex(0);
396 			} else {
397 				cbSourceLevel.setSelectedItem(sourceLevel);
398 			}
399 
400 			cbSourceLevel.setToolTipText(Objects.toString(cbSourceLevel.getSelectedItem(), null));
401 		}
402 
403 		private void loadEclipseFormatterFileForPreview(String formatterFile, String activeProfile) {
404 			String filePath;
405 
406 			try {
407 				URL url = new URL(formatterFile);
408 
409 				filePath = url.toString();
410 			} catch (IOException ex) {
411 				Path formatterFilePath = Paths.get(formatterFile);
412 				if (!formatterFilePath.isAbsolute() && project != null) {
413 					formatterFilePath = Paths.get(project.getProjectDirectory().getPath()).resolve(formatterFilePath);
414 				}
415 
416 				filePath = formatterFilePath.toAbsolutePath().toString();
417 			}
418 
419 			formatterLocField.setText(formatterFile);
420 
421 			cbProfile.setEnabled(false);
422 
423 			cbProfile.removeAllItems();
424 			cbProfile.setToolTipText(null);
425 
426 			try {
427 				//only xml configurations contain profiles
428 				if (EclipseJavaFormatterSettings.isXMLConfigurationFile(filePath)) {
429 					List<String> profileNames = ConfigReader.getProfileNames(ConfigReader.readContentFromFilePath(filePath));
430 					cbProfile.addItem(NbBundle.getMessage(EclipseJavaFormatterOptionsPanel.class, "EclipseJavaFormatterOptionsPanel.chooseProfile"));
431 
432 					String entryToSelect = null;
433 					for (String profileName : profileNames) {
434 						cbProfile.addItem(profileName);
435 						if (activeProfile != null && activeProfile.equals(profileName)) {
436 							entryToSelect = profileName;
437 						}
438 					}
439 					selectProfileOrFallback(entryToSelect, profileNames);
440 					cbProfile.setEnabled(true);
441 				}
442 
443 				formatterLocField.setToolTipText(filePath);
444 			} catch (IOException | SAXException | ConfigReadException ex) {
445 				log.log(Level.INFO, "Could not parse formatter config", ex);
446 			}
447 		}
448 
449 		private void selectProfileOrFallback(String entryToSelect, List<String> profiles) {
450 			if (null != entryToSelect) {
451 				cbProfile.setSelectedItem(entryToSelect);
452 			} else if (profiles.size() == 1) {
453 				//only one entry (excl. default) -> choose the only valid item
454 				cbProfile.setSelectedIndex(1);
455 			} else {
456 				//fallback: ===choose profile==
457 				cbProfile.setSelectedIndex(0);
458 			}
459 
460 			cbProfile.setToolTipText(Objects.toString(cbProfile.getSelectedItem(), null));
461 		}
462 
463 		/**
464 		 * {@inheritDoc}
465 		 */
466 		@Override
467 		public void store(Preferences preferences) {
468 			preferences.put(EclipseJavaFormatterSettings.CONFIG_FILE_LOCATION, formatterLocField.getText());
469 			preferences.put(EclipseJavaFormatterSettings.ACTIVE_PROFILE, getSelectedProfile());
470 			preferences.putBoolean(EclipseJavaFormatterSettings.USE_PROJECT_PREFS, cbUseProjectPref.isSelected());
471 			preferences.put(EclipseJavaFormatterSettings.LINEFEED, getLinefeed());
472 			preferences.put(EclipseJavaFormatterSettings.SOURCELEVEL, getSourceLevel());
473 		}
474 
475 		/**
476 		 * {@inheritDoc}
477 		 */
478 		@Override
479 		public boolean valid() {
480 			errorLabel.setText("");
481 
482 			String fileName = formatterLocField.getText();
483 			if (StringUtils.isBlank(fileName) && cbUseProjectPref.isSelected()) {
484 				// use configuration from .settings
485 				return true;
486 			} else if (StringUtils.isNotBlank(fileName) && project != null) {
487 				Path formatterFilePath = Paths.get(fileName);
488 				if (!formatterFilePath.isAbsolute()) {
489 					fileName = Paths.get(project.getProjectDirectory().getPath()).resolve(formatterFilePath).toString();
490 				}
491 			}
492 
493 			boolean fileExists;
494 			try {
495 				new URL(formatterLocField.getText());
496 
497 				fileName = formatterLocField.getText();
498 				fileExists = true;
499 			} catch (IOException ex) {
500 				fileExists = new File(fileName).exists();
501 			}
502 
503 			boolean isXML = EclipseJavaFormatterSettings.isXMLConfigurationFile(fileName);
504 			boolean isEPF = EclipseJavaFormatterSettings.isWorkspaceMechanicFile(fileName);
505 			boolean isProjectSetting = EclipseJavaFormatterSettings.isProjectSetting(fileName);
506 
507 			if (!fileExists || (!isXML && !isEPF && !isProjectSetting) || (isXML && cbProfile.getSelectedIndex() < 0)) {
508 				errorLabel.setText("Invalid file. Please enter a valid configuration file.");
509 				return false;
510 			} else {
511 				return !isXML || cbProfile.getSelectedIndex() != 0;
512 			}
513 		}
514 	}