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    * markiewb - initial API and implementation and/or initial documentation
9    * bahlef
10   */
11  package de.funfried.netbeans.plugins.external.formatter.java.eclipse;
12  
13  import java.util.Map;
14  import java.util.Objects;
15  import java.util.SortedSet;
16  import java.util.prefs.Preferences;
17  
18  import javax.swing.text.BadLocationException;
19  import javax.swing.text.Document;
20  import javax.swing.text.StyledDocument;
21  
22  import org.apache.commons.lang3.tuple.Pair;
23  import org.netbeans.api.annotations.common.CheckForNull;
24  import org.netbeans.api.annotations.common.NonNull;
25  import org.netbeans.api.project.Project;
26  import org.openide.util.NbBundle;
27  import org.openide.util.lookup.ServiceProvider;
28  
29  import de.funfried.netbeans.plugins.external.formatter.FormatterService;
30  import de.funfried.netbeans.plugins.external.formatter.java.base.AbstractJavaFormatterService;
31  import de.funfried.netbeans.plugins.external.formatter.java.eclipse.ui.EclipseJavaFormatterOptionsPanel;
32  import de.funfried.netbeans.plugins.external.formatter.ui.options.FormatterOptionsPanel;
33  import de.funfried.netbeans.plugins.external.formatter.ui.options.Settings;
34  
35  /**
36   * Eclipse implementation of the {@link AbstractJavaFormatterService}.
37   *
38   * @author markiewb
39   * @author bahlef
40   */
41  @NbBundle.Messages({
42  		"FormatterName=Eclipse Java Code Formatter"
43  })
44  @ServiceProvider(service = FormatterService.class, position = 1000)
45  public class EclipseJavaFormatterService extends AbstractJavaFormatterService<EclipseFormatJob> {
46  	/** The ID of this formatter service. */
47  	public static final String ID = "eclipse-java-formatter";
48  
49  	/** * The {@link EclipseJavaFormatterWrapper} implementation. */
50  	private final EclipseJavaFormatterWrapper formatter = new EclipseJavaFormatterWrapper();
51  
52  	/**
53  	 * {@inheritDoc}
54  	 */
55  	@NonNull
56  	@Override
57  	public String getDisplayName() {
58  		return NbBundle.getMessage(EclipseJavaFormatterService.class, "FormatterName");
59  	}
60  
61  	/**
62  	 * {@inheritDoc}
63  	 */
64  	@NonNull
65  	@Override
66  	public String getId() {
67  		return ID;
68  	}
69  
70  	/**
71  	 * {@inheritDoc}
72  	 */
73  	@Override
74  	public FormatterOptionsPanel createOptionsPanel(Project project) {
75  		return new EclipseJavaFormatterOptionsPanel(project);
76  	}
77  
78  	/**
79  	 * {@inheritDoc}
80  	 */
81  	@CheckForNull
82  	@Override
83  	public Integer getContinuationIndentSize(Document document) {
84  		if (document == null) {
85  			return null;
86  		}
87  
88  		Integer ret = null;
89  
90  		Preferences preferences = Settings.getActivePreferences(document);
91  		if (isUseFormatterIndentationSettings(preferences)) {
92  			String value = getEclipseFormatterProperty(preferences, document, "org.eclipse.jdt.core.formatter.continuation_indentation");
93  			if (value != null) {
94  				ret = Integer.valueOf(value);
95  
96  				Integer indentSize = getIndentSize(document);
97  				if (indentSize != null) {
98  					ret *= indentSize;
99  				}
100 			}
101 		}
102 
103 		return ret;
104 	}
105 
106 	/**
107 	 * {@inheritDoc}
108 	 */
109 	@CheckForNull
110 	@Override
111 	public Integer getIndentSize(Document document) {
112 		if (document == null) {
113 			return null;
114 		}
115 
116 		Integer ret = null;
117 
118 		Preferences preferences = Settings.getActivePreferences(document);
119 		if (isUseFormatterIndentationSettings(preferences)) {
120 			String tabChar = getEclipseFormatterProperty(preferences, document, "org.eclipse.jdt.core.formatter.tabulation.char");
121 			if (Objects.equals(tabChar, "mixed")) {
122 				String value = getEclipseFormatterProperty(preferences, document, "org.eclipse.jdt.core.formatter.indentation.size");
123 				if (value != null) {
124 					ret = Integer.valueOf(value);
125 				}
126 			} else {
127 				ret = getSpacesPerTab(document);
128 			}
129 		}
130 
131 		return ret;
132 	}
133 
134 	/**
135 	 * {@inheritDoc}
136 	 */
137 	@CheckForNull
138 	@Override
139 	public Integer getRightMargin(Document document) {
140 		if (document == null) {
141 			return null;
142 		}
143 
144 		Integer ret = null;
145 
146 		String value = getEclipseFormatterProperty(null, document, "org.eclipse.jdt.core.formatter.lineSplit");
147 		if (value != null) {
148 			ret = Integer.valueOf(value);
149 		}
150 
151 		return ret;
152 	}
153 
154 	/**
155 	 * {@inheritDoc}
156 	 */
157 	@Override
158 	protected EclipseFormatJob getFormatJob(StyledDocument document, SortedSet<Pair<Integer, Integer>> changedElements) {
159 		return new EclipseFormatJob(document, formatter, changedElements);
160 	}
161 
162 	/**
163 	 * {@inheritDoc}
164 	 */
165 	@CheckForNull
166 	@Override
167 	public Integer getSpacesPerTab(Document document) {
168 		if (document == null) {
169 			return null;
170 		}
171 
172 		Integer ret = null;
173 
174 		Preferences preferences = Settings.getActivePreferences(document);
175 		if (isUseFormatterIndentationSettings(preferences)) {
176 			if (!isExpandTabToSpaces(document, preferences) && preferences.getBoolean(Settings.OVERRIDE_TAB_SIZE, true)) {
177 				ret = preferences.getInt(Settings.OVERRIDE_TAB_SIZE_VALUE, 4);
178 			} else {
179 				String value = getEclipseFormatterProperty(preferences, document, "org.eclipse.jdt.core.formatter.tabulation.size");
180 				if (value != null) {
181 					ret = Integer.valueOf(value);
182 				}
183 			}
184 		}
185 
186 		return ret;
187 	}
188 
189 	/**
190 	 * Reads the configuration value of the Eclipse formatter configuration from a given
191 	 * {@link Document} for the given {@code  key}.
192 	 *
193 	 * @param preferences the {@link Preferences} of the {@link Document} if already loaded
194 	 *        or {@code null} to read the preferences of the given {@link Document}
195 	 * @param document the {@link Document} where to read the value from
196 	 * @param key the key of the value which should be read
197 	 *
198 	 * @return the configuration value of the Eclipse formatter configuration from a given
199 	 *         {@link Document} for the given {@code  key}
200 	 */
201 	private String getEclipseFormatterProperty(Preferences preferences, Document document, String key) {
202 		if (document == null) {
203 			return null;
204 		}
205 
206 		if (preferences == null) {
207 			preferences = Settings.getActivePreferences(document);
208 		}
209 
210 		String formatterFile = EclipseJavaFormatterSettings.getEclipseFormatterFile(preferences, document);
211 		String formatterProfile = preferences.get(EclipseJavaFormatterSettings.ACTIVE_PROFILE, "");
212 		String sourceLevel = preferences.get(EclipseJavaFormatterSettings.SOURCELEVEL, "");
213 
214 		Map<String, String> config = EclipseFormatterConfig.parseConfig(formatterFile, formatterProfile, sourceLevel);
215 
216 		return config.getOrDefault(key, null);
217 	}
218 
219 	/**
220 	 * {@inheritDoc}
221 	 */
222 	@CheckForNull
223 	@Override
224 	public Boolean isExpandTabToSpaces(Document document) {
225 		if (document == null) {
226 			return null;
227 		}
228 
229 		return isExpandTabToSpaces(document, Settings.getActivePreferences(document));
230 	}
231 
232 	private Boolean isExpandTabToSpaces(Document document, Preferences preferences) {
233 		if (document == null || preferences == null) {
234 			return null;
235 		}
236 
237 		Boolean ret = null;
238 
239 		if (isUseFormatterIndentationSettings(preferences)) {
240 			String value = getEclipseFormatterProperty(preferences, document, "org.eclipse.jdt.core.formatter.tabulation.char");
241 			if (value != null) {
242 				ret = Objects.equals(value, "space");
243 			}
244 		}
245 
246 		return ret;
247 	}
248 
249 	/**
250 	 * Returns {@code true} if using the formatter indentation settings from the external
251 	 * formatter is activated, otherwise {@code false}.
252 	 *
253 	 * @param prefs the {@link Preferences} where to check
254 	 *
255 	 * @return {@code true} if using the formatter indentation settings from the external
256 	 *         formatter is activated, otherwise {@code false}
257 	 */
258 	private boolean isUseFormatterIndentationSettings(Preferences prefs) {
259 		return prefs.getBoolean(Settings.ENABLE_USE_OF_INDENTATION_SETTINGS, true);
260 	}
261 
262 	/**
263 	 * {@inheritDoc}
264 	 */
265 	@Override
266 	@CheckForNull
267 	public Boolean organizeImports(StyledDocument document, boolean afterFixImports) throws BadLocationException {
268 		return null;
269 	}
270 }