blob: f525d5f2119c553631c8b04ff0a1094feaaac4e3 [file] [log] [blame]
david_williamscfdb2cd2004-11-11 08:37:49 +00001/*******************************************************************************
amywueb0283b2007-10-16 19:16:45 +00002 * Copyright (c) 2001, 2007 IBM Corporation and others.
david_williamscfdb2cd2004-11-11 08:37:49 +00003 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
amywuecebb042007-04-10 20:07:35 +00007 *
david_williamscfdb2cd2004-11-11 08:37:49 +00008 * Contributors:
9 * IBM Corporation - initial API and implementation
10 * Jens Lukowski/Innoopract - initial renaming/restructuring
11 *
12 *******************************************************************************/
david_williamsd8a74fb2005-03-18 02:41:46 +000013package org.eclipse.wst.sse.core.internal.cleanup;
david_williamscfdb2cd2004-11-11 08:37:49 +000014
15import java.io.ByteArrayInputStream;
16import java.io.FileInputStream;
17import java.io.IOException;
18import java.io.InputStream;
19import java.io.OutputStream;
nitind7cd174c2005-01-18 20:03:58 +000020import java.util.Map;
david_williamscfdb2cd2004-11-11 08:37:49 +000021import java.util.Vector;
22
23import org.eclipse.core.resources.IFile;
24import org.eclipse.core.runtime.CoreException;
nitind861c8e32008-02-28 05:51:36 +000025import org.eclipse.core.runtime.Platform;
26import org.eclipse.core.runtime.preferences.IScopeContext;
27import org.eclipse.core.runtime.preferences.InstanceScope;
david_williamscfdb2cd2004-11-11 08:37:49 +000028import org.eclipse.jface.text.BadLocationException;
amywueb0283b2007-10-16 19:16:45 +000029import org.eclipse.jface.text.DocumentRewriteSession;
30import org.eclipse.jface.text.DocumentRewriteSessionType;
david_williamscfdb2cd2004-11-11 08:37:49 +000031import org.eclipse.jface.text.IDocument;
amywueb0283b2007-10-16 19:16:45 +000032import org.eclipse.jface.text.IDocumentExtension4;
david_williamscfdb2cd2004-11-11 08:37:49 +000033import org.eclipse.jface.text.IRegion;
nitind7cd174c2005-01-18 20:03:58 +000034import org.eclipse.jface.text.TextUtilities;
david_williamsb5d05632006-02-27 09:24:00 +000035import org.eclipse.wst.sse.core.StructuredModelManager;
david_williamscfdb2cd2004-11-11 08:37:49 +000036import org.eclipse.wst.sse.core.internal.Logger;
pavery40dd89e2005-04-01 21:09:29 +000037import org.eclipse.wst.sse.core.internal.format.IStructuredFormatProcessor;
david_williams4ad020f2005-04-18 08:00:30 +000038import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
david_williamscfdb2cd2004-11-11 08:37:49 +000039import org.w3c.dom.Attr;
40import org.w3c.dom.Node;
41
42
43public abstract class AbstractStructuredCleanupProcessor implements IStructuredCleanupProcessor {
44 public boolean refreshCleanupPreferences = true; // special flag for JUnit
45
46 // tests to skip refresh
47 // of cleanup preferences
48 // when it's set to false
49
50 public String cleanupContent(String input) throws IOException, CoreException {
51 IStructuredModel structuredModel = null;
52 InputStream inputStream = null;
53 try {
54 // setup structuredModel
55 inputStream = new ByteArrayInputStream(input.getBytes("UTF8")); //$NON-NLS-1$
56 String id = inputStream.toString() + getContentType();
nitindecd7bfc2004-12-29 20:58:48 +000057 structuredModel = StructuredModelManager.getModelManager().getModelForRead(id, inputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +000058
59 // cleanup
60 cleanupModel(structuredModel, 0, structuredModel.getStructuredDocument().getLength());
61
62 // return output
63 return structuredModel.getStructuredDocument().get();
64 } finally {
65 ensureClosed(null, inputStream);
66 // release from model manager
67 if (structuredModel != null)
68 structuredModel.releaseFromRead();
69 }
70 }
71
72 public String cleanupContent(String input, int start, int length) throws IOException, CoreException {
73 IStructuredModel structuredModel = null;
74 InputStream inputStream = null;
75 try {
76 // setup structuredModel
77 inputStream = new ByteArrayInputStream(input.getBytes("UTF8")); //$NON-NLS-1$
78 String id = inputStream.toString() + getContentType();
nitindecd7bfc2004-12-29 20:58:48 +000079 structuredModel = StructuredModelManager.getModelManager().getModelForRead(id, inputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +000080
81 // cleanup
82 cleanupModel(structuredModel, start, length);
83
84 // return output
85 return structuredModel.getStructuredDocument().get();
86 } finally {
87 ensureClosed(null, inputStream);
88 // release from model manager
89 if (structuredModel != null)
90 structuredModel.releaseFromRead();
91 }
92 }
93
94 public void cleanupDocument(IDocument document) throws IOException, CoreException {
95 if (document == null)
96 return;
97
98 IStructuredModel structuredModel = null;
amywueb0283b2007-10-16 19:16:45 +000099 // OutputStream outputStream = null;
david_williamscfdb2cd2004-11-11 08:37:49 +0000100 try {
101 // setup structuredModel
102 // Note: We are getting model for edit. Will save model if model
103 // changed.
nitindecd7bfc2004-12-29 20:58:48 +0000104 structuredModel = StructuredModelManager.getModelManager().getExistingModelForEdit(document);
david_williamscfdb2cd2004-11-11 08:37:49 +0000105
106 // cleanup
107 cleanupModel(structuredModel);
108
109 // save model if needed
110 if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded())
111 structuredModel.save();
112 } finally {
amywueb0283b2007-10-16 19:16:45 +0000113 // ensureClosed(outputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +0000114 // release from model manager
115 if (structuredModel != null)
116 structuredModel.releaseFromEdit();
117 }
118 }
119
120 public void cleanupDocument(IDocument document, int start, int length) throws IOException, CoreException {
121 if (document == null)
122 return;
123
124 if (start >= 0 && length >= 0 && start + length <= document.getLength()) {
125 IStructuredModel structuredModel = null;
amywueb0283b2007-10-16 19:16:45 +0000126 // OutputStream outputStream = null;
david_williamscfdb2cd2004-11-11 08:37:49 +0000127 try {
128 // setup structuredModel
129 // Note: We are getting model for edit. Will save model if
130 // model changed.
nitindecd7bfc2004-12-29 20:58:48 +0000131 structuredModel = StructuredModelManager.getModelManager().getExistingModelForEdit(document);
david_williamscfdb2cd2004-11-11 08:37:49 +0000132
133 // cleanup
134 cleanupModel(structuredModel, start, length);
135
136 // save model if needed
137 if (!structuredModel.isSharedForEdit() && structuredModel.isSaveNeeded())
138 structuredModel.save();
139 } finally {
amywueb0283b2007-10-16 19:16:45 +0000140 // ensureClosed(outputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +0000141 // release from model manager
142 if (structuredModel != null)
143 structuredModel.releaseFromEdit();
144 }
145 }
146 }
147
148 public void cleanupFile(IFile file) throws IOException, CoreException {
149 IStructuredModel structuredModel = null;
amywueb0283b2007-10-16 19:16:45 +0000150 // OutputStream outputStream = null;
david_williamscfdb2cd2004-11-11 08:37:49 +0000151 try {
152 // setup structuredModel
nitindecd7bfc2004-12-29 20:58:48 +0000153 structuredModel = StructuredModelManager.getModelManager().getModelForRead(file);
david_williamscfdb2cd2004-11-11 08:37:49 +0000154
155 // cleanup
156 cleanupModel(structuredModel, 0, structuredModel.getStructuredDocument().getLength());
157
158 // save output to file
amywueb0283b2007-10-16 19:16:45 +0000159 // outputStream = new
david_williamscfdb2cd2004-11-11 08:37:49 +0000160 // FileOutputStream(file.getLocation().toString());
161 structuredModel.save(file);
162 } finally {
amywueb0283b2007-10-16 19:16:45 +0000163 // ensureClosed(outputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +0000164 // release from model manager
165 if (structuredModel != null)
166 structuredModel.releaseFromRead();
167 }
168 }
169
170 public void cleanupFile(IFile file, int start, int length) throws IOException, CoreException {
171 IStructuredModel structuredModel = null;
amywueb0283b2007-10-16 19:16:45 +0000172 // OutputStream outputStream = null;
david_williamscfdb2cd2004-11-11 08:37:49 +0000173 try {
174 // setup structuredModel
nitindecd7bfc2004-12-29 20:58:48 +0000175 structuredModel = StructuredModelManager.getModelManager().getModelForRead(file);
david_williamscfdb2cd2004-11-11 08:37:49 +0000176
177 // cleanup
178 cleanupModel(structuredModel, start, length);
179
180 // save output to file
amywueb0283b2007-10-16 19:16:45 +0000181 // outputStream = new
david_williamscfdb2cd2004-11-11 08:37:49 +0000182 // FileOutputStream(file.getLocation().toString());
183 structuredModel.save(file);
184 } finally {
amywueb0283b2007-10-16 19:16:45 +0000185 // ensureClosed(outputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +0000186 // release from model manager
187 if (structuredModel != null)
188 structuredModel.releaseFromRead();
189 }
190 }
191
192 public void cleanupFileName(String fileName) throws IOException, CoreException {
193 IStructuredModel structuredModel = null;
194 InputStream inputStream = null;
amywueb0283b2007-10-16 19:16:45 +0000195 // OutputStream outputStream = null;
david_williamscfdb2cd2004-11-11 08:37:49 +0000196 try {
197 // setup structuredModel
198 inputStream = new FileInputStream(fileName);
nitindecd7bfc2004-12-29 20:58:48 +0000199 structuredModel = StructuredModelManager.getModelManager().getModelForRead(fileName, inputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +0000200
201 // cleanup
202 cleanupModel(structuredModel, 0, structuredModel.getStructuredDocument().getLength());
203
204 // save output to file
amywueb0283b2007-10-16 19:16:45 +0000205 // outputStream = new FileOutputStream(fileName);
david_williamscfdb2cd2004-11-11 08:37:49 +0000206 structuredModel.save();
207 } finally {
amywueb0283b2007-10-16 19:16:45 +0000208 // ensureClosed(outputStream, inputStream);
david_williamscfdb2cd2004-11-11 08:37:49 +0000209 // release from model manager
210 if (structuredModel != null)
211 structuredModel.releaseFromRead();
212 }
213 }
214
215 public void cleanupFileName(String fileName, int start, int length) throws IOException, CoreException {
216 IStructuredModel structuredModel = null;
217 InputStream inputStream = null;
amywueb0283b2007-10-16 19:16:45 +0000218 // OutputStream outputStream = null;
david_williamscfdb2cd2004-11-11 08:37:49 +0000219 try {
220 // setup structuredModel
221 inputStream = new FileInputStream(fileName);
nitindecd7bfc2004-12-29 20:58:48 +0000222 structuredModel = StructuredModelManager.getModelManager().getModelForRead(fileName, inputStream, null);
david_williamscfdb2cd2004-11-11 08:37:49 +0000223
224 // cleanup
225 cleanupModel(structuredModel, start, length);
226
227 // save output to file
amywueb0283b2007-10-16 19:16:45 +0000228 // outputStream = new FileOutputStream(fileName);
david_williamscfdb2cd2004-11-11 08:37:49 +0000229 structuredModel.save();
230 } finally {
amywueb0283b2007-10-16 19:16:45 +0000231 // ensureClosed(outputStream, inputStream);
david_williamscfdb2cd2004-11-11 08:37:49 +0000232 // release from model manager
233 if (structuredModel != null)
234 structuredModel.releaseFromRead();
235 }
236 }
237
238 public void cleanupModel(IStructuredModel structuredModel) {
239
240 int start = 0;
241 int length = structuredModel.getStructuredDocument().getLength();
242 cleanupModel(structuredModel, start, length);
243 }
244
245 public void cleanupModel(IStructuredModel structuredModel, int start, int length) {
246
247 if (structuredModel != null) {
248 if ((start >= 0) && (length <= structuredModel.getStructuredDocument().getLength())) {
249 Vector activeNodes = getActiveNodes(structuredModel, start, length);
250 if (activeNodes.size() > 0) {
251 Node firstNode = (Node) activeNodes.firstElement();
252 Node lastNode = (Node) activeNodes.lastElement();
253 boolean done = false;
254 Node eachNode = firstNode;
255 Node nextNode = null;
amywueb0283b2007-10-16 19:16:45 +0000256
257 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=123621
258 // if doing any sort of cleanup, set up rewrite session/modelchanged
259 IDocumentExtension4 docExt4 = null;
260 if (structuredModel.getStructuredDocument() instanceof IDocumentExtension4) {
261 docExt4 = (IDocumentExtension4) structuredModel.getStructuredDocument();
262 }
263 DocumentRewriteSession rewriteSession = null;
264
265 try {
266 // whenever formatting model, fire
267 // abouttochange/modelchanged
268 structuredModel.aboutToChangeModel();
269 rewriteSession = (docExt4 == null || docExt4.getActiveRewriteSession() != null) ? null : docExt4.startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED);
270
271 while (!done) {
272 // update "done"
273 done = (eachNode == lastNode);
david_williamscfdb2cd2004-11-11 08:37:49 +0000274
275 // get next sibling before cleanup because eachNode
amywueb0283b2007-10-16 19:16:45 +0000276 // may
277 // be deleted,
278 // for example when it's an empty text node
279 nextNode = eachNode.getNextSibling();
david_williamscfdb2cd2004-11-11 08:37:49 +0000280
amywueb0283b2007-10-16 19:16:45 +0000281 // cleanup selected node(s)
282 cleanupNode(eachNode);
david_williamscfdb2cd2004-11-11 08:37:49 +0000283
amywueb0283b2007-10-16 19:16:45 +0000284 // update each node
285 if (nextNode != null && nextNode.getParentNode() == null)
286 // nextNode is deleted during cleanup
287 eachNode = eachNode.getNextSibling();
288 else
289 eachNode = nextNode;
david_williamscfdb2cd2004-11-11 08:37:49 +0000290
amywueb0283b2007-10-16 19:16:45 +0000291 // This should not be needed, but just in case
292 // something went wrong with with eachNode.
293 // We don't want an infinite loop here.
294 if (eachNode == null)
295 done = true;
296 }
david_williamscfdb2cd2004-11-11 08:37:49 +0000297
amywueb0283b2007-10-16 19:16:45 +0000298 // format source
299 if (getFormatSourcePreference(structuredModel)) {
300 // format the document
301 IStructuredFormatProcessor formatProcessor = getFormatProcessor();
302 formatProcessor.formatModel(structuredModel);
303 }
amywueb0283b2007-10-16 19:16:45 +0000304 }
305 finally {
306 // we need two finally's, just in case first fails
307 try {
308 if ((docExt4 != null) && (rewriteSession != null))
309 docExt4.stopRewriteSession(rewriteSession);
310 }
311 finally {
312 // always make sure to fire changedmodel when done
313 structuredModel.changedModel();
david_williamscfdb2cd2004-11-11 08:37:49 +0000314 }
315 }
316 }
317 }
318 }
319 }
320
321 public void cleanupNode(Node node) {
322 if (node != null) {
323 Node cleanupNode = node;
324
325 // cleanup the owner node if it's an attribute node
326 if (cleanupNode.getNodeType() == Node.ATTRIBUTE_NODE)
327 cleanupNode = ((Attr) cleanupNode).getOwnerElement();
328
329 // refresh cleanup preferences before getting cleanup handler
330 if (refreshCleanupPreferences)
331 refreshCleanupPreferences();
332
333 // get cleanup handler
334 IStructuredCleanupHandler cleanupHandler = getCleanupHandler(cleanupNode);
335 if (cleanupHandler != null) {
336 // cleanup each node
337 cleanupHandler.cleanup(cleanupNode);
338 }
339 }
340 }
341
342 protected void convertLineDelimiters(IDocument document, String newDelimiter) {
david_williamscfdb2cd2004-11-11 08:37:49 +0000343 final int lineCount = document.getNumberOfLines();
amywueb0283b2007-10-16 19:16:45 +0000344 Map partitioners = TextUtilities.removeDocumentPartitioners(document);
david_williamscfdb2cd2004-11-11 08:37:49 +0000345 try {
346 for (int i = 0; i < lineCount; i++) {
347 final String delimiter = document.getLineDelimiter(i);
348 if (delimiter != null && delimiter.length() > 0 && !delimiter.equals(newDelimiter)) {
349 IRegion region = document.getLineInformation(i);
350 document.replace(region.getOffset() + region.getLength(), delimiter.length(), newDelimiter);
351 }
352 }
353 } catch (BadLocationException e) {
354 Logger.logException(e);
355 } finally {
nitind7cd174c2005-01-18 20:03:58 +0000356 TextUtilities.addDocumentPartitioners(document, partitioners);
david_williamscfdb2cd2004-11-11 08:37:49 +0000357 }
358 }
359
360 protected void ensureClosed(OutputStream outputStream, InputStream inputStream) {
361 try {
362 if (inputStream != null) {
363 inputStream.close();
364 }
365 } catch (IOException e) {
366 Logger.logException(e); // hopeless
367 }
368 try {
369 if (outputStream != null) {
370 outputStream.close();
371 }
372 } catch (IOException e) {
373 Logger.logException(e); // hopeless
374 }
375 }
376
377 protected Vector getActiveNodes(IStructuredModel structuredModel, int startNodeOffset, int length) {
378 Vector activeNodes = new Vector();
379
380 if (structuredModel != null) {
381 Node startNode = (Node) structuredModel.getIndexedRegion(startNodeOffset);
382 Node endNode = (Node) structuredModel.getIndexedRegion(startNodeOffset + length);
383
384 // make sure it's an non-empty document
385 if (startNode != null) {
386 while (isSiblingOf(startNode, endNode) == false) {
387 if (endNode != null)
388 endNode = endNode.getParentNode();
389 if (endNode == null) {
390 startNode = startNode.getParentNode();
391 endNode = (Node) structuredModel.getIndexedRegion(startNodeOffset + length);
392 }
393 }
394
395 while (startNode != endNode) {
396 activeNodes.addElement(startNode);
397 startNode = startNode.getNextSibling();
398 }
399 if (startNode != null)
400 activeNodes.addElement(startNode);
401 }
402 }
403
404 return activeNodes;
405 }
406
407 abstract protected IStructuredCleanupHandler getCleanupHandler(Node node);
408
409 abstract protected String getContentType();
410
411 protected boolean getConvertEOLCodesPreference(IStructuredModel structuredModel) {
412
413 boolean convertEOLCodes = true;
414 IStructuredCleanupHandler cleanupHandler = getCleanupHandler((Node) structuredModel.getIndexedRegion(0));
415 if (cleanupHandler != null) {
416 IStructuredCleanupPreferences cleanupPreferences = cleanupHandler.getCleanupPreferences();
417 convertEOLCodes = cleanupPreferences.getConvertEOLCodes();
418 }
419 return convertEOLCodes;
420 }
421
422 protected String getEOLCodePreference(IStructuredModel structuredModel) {
423
nitind861c8e32008-02-28 05:51:36 +0000424 IScopeContext[] scopeContext = new IScopeContext[]{new InstanceScope()};
425 String eolCode = Platform.getPreferencesService().getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
david_williamscfdb2cd2004-11-11 08:37:49 +0000426
427 IStructuredCleanupHandler cleanupHandler = getCleanupHandler((Node) structuredModel.getIndexedRegion(0));
428 if (cleanupHandler != null) {
429 IStructuredCleanupPreferences cleanupPreferences = cleanupHandler.getCleanupPreferences();
430 eolCode = cleanupPreferences.getEOLCode();
431 }
432 return eolCode;
433 }
434
435 abstract protected IStructuredFormatProcessor getFormatProcessor();
436
437 protected boolean getFormatSourcePreference(IStructuredModel structuredModel) {
438
439 boolean formatSource = true;
440 IStructuredCleanupHandler cleanupHandler = getCleanupHandler((Node) structuredModel.getIndexedRegion(0));
441 if (cleanupHandler != null) {
442 IStructuredCleanupPreferences cleanupPreferences = cleanupHandler.getCleanupPreferences();
443 formatSource = cleanupPreferences.getFormatSource();
444 }
445 return formatSource;
446 }
447
david_williamscfdb2cd2004-11-11 08:37:49 +0000448 protected boolean isSiblingOf(Node node, Node endNode) {
449 if (endNode == null) {
450 return true;
451 } else {
452 Node siblingNode = node;
453 while (siblingNode != null) {
454 if (siblingNode == endNode)
455 return true;
456 else
457 siblingNode = siblingNode.getNextSibling();
458 }
459 return false;
460 }
461 }
462
463 abstract protected void refreshCleanupPreferences();
464}