001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.editors.text;
011:
012: import java.io.BufferedReader;
013: import java.io.IOException;
014: import java.io.InputStream;
015: import java.io.InputStreamReader;
016: import java.io.Reader;
017:
018: import org.osgi.framework.Bundle;
019:
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.ILog;
022: import org.eclipse.core.runtime.IPath;
023: import org.eclipse.core.runtime.IProgressMonitor;
024: import org.eclipse.core.runtime.IStatus;
025: import org.eclipse.core.runtime.MultiStatus;
026: import org.eclipse.core.runtime.Platform;
027: import org.eclipse.core.runtime.QualifiedName;
028: import org.eclipse.core.runtime.Status;
029: import org.eclipse.core.runtime.content.IContentDescription;
030: import org.eclipse.core.runtime.content.IContentType;
031:
032: import org.eclipse.core.resources.IEncodedStorage;
033: import org.eclipse.core.resources.IResourceStatus;
034: import org.eclipse.core.resources.IStorage;
035: import org.eclipse.core.resources.ResourcesPlugin;
036:
037: import org.eclipse.jface.operation.IRunnableContext;
038:
039: import org.eclipse.jface.text.Document;
040: import org.eclipse.jface.text.IDocument;
041: import org.eclipse.jface.text.source.IAnnotationModel;
042:
043: import org.eclipse.ui.IEditorInput;
044: import org.eclipse.ui.IStorageEditorInput;
045: import org.eclipse.ui.PlatformUI;
046: import org.eclipse.ui.internal.editors.text.EditorsPlugin;
047: import org.eclipse.ui.internal.editors.text.NLSUtility;
048: import org.eclipse.ui.texteditor.AbstractDocumentProvider;
049:
050: /**
051: * Shared document provider specialized for {@link org.eclipse.core.resources.IStorage}s.
052: */
053: public class StorageDocumentProvider extends AbstractDocumentProvider
054: implements IStorageDocumentProvider {
055:
056: /**
057: * Default file size.
058: *
059: * @since 2.1
060: */
061: protected static final int DEFAULT_FILE_SIZE = 15 * 1024;
062:
063: /**
064: * Constant denoting an empty set of properties
065: * @since 3.1
066: */
067: private static final QualifiedName[] NO_PROPERTIES = new QualifiedName[0];
068:
069: /**
070: * Bundle of all required information to allow {@link org.eclipse.core.resources.IStorage} as underlying document resources.
071: * @since 2.0
072: */
073: protected class StorageInfo extends ElementInfo {
074:
075: /** The flag representing the cached state whether the storage is modifiable. */
076: public boolean fIsModifiable = false;
077: /** The flag representing the cached state whether the storage is read-only. */
078: public boolean fIsReadOnly = true;
079: /** The flag representing the need to update the cached flag. */
080: public boolean fUpdateCache = true;
081: /** The encoding used to create the document from the storage or <code>null</code> for workbench encoding. */
082: public String fEncoding;
083:
084: /**
085: * Creates a new storage info.
086: *
087: * @param document the document
088: * @param model the annotation model
089: */
090: public StorageInfo(IDocument document, IAnnotationModel model) {
091: super (document, model);
092: fEncoding = null;
093: }
094: }
095:
096: /**
097: * Creates a new document provider.
098: *
099: * @since 2.0
100: */
101: public StorageDocumentProvider() {
102: super ();
103: }
104:
105: /**
106: * Initializes the given document with the given stream.
107: *
108: * @param document the document to be initialized
109: * @param contentStream the stream which delivers the document content
110: * @throws CoreException if the given stream can not be read
111: *
112: * @deprecated use encoding based version instead
113: */
114: protected void setDocumentContent(IDocument document,
115: InputStream contentStream) throws CoreException {
116: setDocumentContent(document, contentStream, null);
117: }
118:
119: /**
120: * Initializes the given document with the given stream using the given encoding.
121: *
122: * @param document the document to be initialized
123: * @param contentStream the stream which delivers the document content
124: * @param encoding the character encoding for reading the given stream
125: * @throws CoreException if the given stream can not be read
126: * @since 2.0
127: */
128: protected void setDocumentContent(IDocument document,
129: InputStream contentStream, String encoding)
130: throws CoreException {
131:
132: Reader in = null;
133:
134: try {
135:
136: if (encoding == null)
137: encoding = getDefaultEncoding();
138:
139: in = new BufferedReader(new InputStreamReader(
140: contentStream, encoding), DEFAULT_FILE_SIZE);
141: StringBuffer buffer = new StringBuffer(DEFAULT_FILE_SIZE);
142: char[] readBuffer = new char[2048];
143: int n = in.read(readBuffer);
144: while (n > 0) {
145: buffer.append(readBuffer, 0, n);
146: n = in.read(readBuffer);
147: }
148:
149: document.set(buffer.toString());
150:
151: } catch (IOException x) {
152: String message = (x.getMessage() != null ? x.getMessage()
153: : ""); //$NON-NLS-1$
154: IStatus s = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
155: IStatus.OK, message, x);
156: throw new CoreException(s);
157: } finally {
158: try {
159: if (in != null)
160: in.close();
161: else
162: contentStream.close();
163: } catch (IOException x) {
164: }
165: }
166: }
167:
168: /**
169: * Initializes the given document from the given editor input using the default character encoding.
170: *
171: * @param document the document to be initialized
172: * @param editorInput the input from which to derive the content of the document
173: * @return <code>true</code> if the document content could be set, <code>false</code> otherwise
174: * @throws CoreException if the given editor input cannot be accessed
175: * @deprecated use the encoding based version instead
176: * @since 2.0
177: */
178: protected boolean setDocumentContent(IDocument document,
179: IEditorInput editorInput) throws CoreException {
180: return setDocumentContent(document, editorInput, null);
181: }
182:
183: /**
184: * Initializes the given document from the given editor input using the given character encoding.
185: *
186: * @param document the document to be initialized
187: * @param editorInput the input from which to derive the content of the document
188: * @param encoding the character encoding used to read the editor input
189: * @return <code>true</code> if the document content could be set, <code>false</code> otherwise
190: * @throws CoreException if the given editor input cannot be accessed
191: * @since 2.0
192: */
193: protected boolean setDocumentContent(IDocument document,
194: IEditorInput editorInput, String encoding)
195: throws CoreException {
196: if (editorInput instanceof IStorageEditorInput) {
197: IStorage storage = ((IStorageEditorInput) editorInput)
198: .getStorage();
199: InputStream stream = storage.getContents();
200: try {
201: setDocumentContent(document, stream, encoding);
202: } finally {
203: try {
204: stream.close();
205: } catch (IOException x) {
206: }
207: }
208: return true;
209: }
210: return false;
211: }
212:
213: /*
214: * @see AbstractDocumentProvider#createAnnotationModel(Object)
215: */
216: protected IAnnotationModel createAnnotationModel(Object element)
217: throws CoreException {
218: return null;
219: }
220:
221: /**
222: * Factory method for creating empty documents.
223: * @return the newly created document
224: * @since 2.1
225: */
226: protected IDocument createEmptyDocument() {
227: return new Document();
228: }
229:
230: /*
231: * @see AbstractDocumentProvider#createDocument(Object)
232: */
233: protected IDocument createDocument(Object element)
234: throws CoreException {
235:
236: if (element instanceof IEditorInput) {
237: IDocument document = createEmptyDocument();
238: if (setDocumentContent(document, (IEditorInput) element,
239: getEncoding(element))) {
240: setupDocument(element, document);
241: return document;
242: }
243: }
244:
245: return null;
246: }
247:
248: /**
249: * Sets up the given document as it would be provided for the given element. The
250: * content of the document is not changed. This default implementation is empty.
251: * Subclasses may reimplement.
252: *
253: * @param element the blue-print element
254: * @param document the document to set up
255: * @since 3.0
256: */
257: protected void setupDocument(Object element, IDocument document) {
258: }
259:
260: /*
261: * @see AbstractDocumentProvider#createElementInfo(Object)
262: * @since 2.0
263: */
264: protected ElementInfo createElementInfo(Object element)
265: throws CoreException {
266: if (element instanceof IStorageEditorInput) {
267:
268: IDocument document = null;
269: IStatus status = null;
270:
271: try {
272: document = createDocument(element);
273: } catch (CoreException x) {
274: status = x.getStatus();
275: document = createEmptyDocument();
276: }
277:
278: ElementInfo info = new StorageInfo(document,
279: createAnnotationModel(element));
280: info.fStatus = status;
281: ((StorageInfo) info).fEncoding = getPersistedEncoding(element);
282:
283: return info;
284: }
285:
286: return super .createElementInfo(element);
287: }
288:
289: /*
290: * @see AbstractDocumentProvider#doSaveDocument(IProgressMonitor, Object, IDocument, boolean)
291: */
292: protected void doSaveDocument(IProgressMonitor monitor,
293: Object element, IDocument document, boolean overwrite)
294: throws CoreException {
295: }
296:
297: /**
298: * Defines the standard procedure to handle <code>CoreExceptions</code>. Exceptions
299: * are written to the plug-in log.
300: *
301: * @param exception the exception to be logged
302: * @param message the message to be logged
303: * @since 2.0
304: */
305: protected void handleCoreException(CoreException exception,
306: String message) {
307:
308: Bundle bundle = Platform.getBundle(PlatformUI.PLUGIN_ID);
309: ILog log = Platform.getLog(bundle);
310:
311: if (message != null)
312: log.log(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID,
313: IStatus.OK, message, exception));
314: else
315: log.log(exception.getStatus());
316: }
317:
318: /**
319: * Updates the internal cache for the given input.
320: *
321: * @param input the input whose cache will be updated
322: * @throws CoreException if the storage cannot be retrieved from the input
323: * @since 2.0
324: */
325: protected void updateCache(IStorageEditorInput input)
326: throws CoreException {
327: StorageInfo info = (StorageInfo) getElementInfo(input);
328: if (info != null) {
329: try {
330: IStorage storage = input.getStorage();
331: if (storage != null) {
332: boolean readOnly = storage.isReadOnly();
333: info.fIsReadOnly = readOnly;
334: info.fIsModifiable = !readOnly;
335: }
336: } catch (CoreException x) {
337: handleCoreException(
338: x,
339: TextEditorMessages.StorageDocumentProvider_updateCache);
340: }
341: info.fUpdateCache = false;
342: }
343: }
344:
345: /*
346: * @see IDocumentProviderExtension#isReadOnly(Object)
347: * @since 2.0
348: */
349: public boolean isReadOnly(Object element) {
350: if (element instanceof IStorageEditorInput) {
351: StorageInfo info = (StorageInfo) getElementInfo(element);
352: if (info != null) {
353: if (info.fUpdateCache) {
354: try {
355: updateCache((IStorageEditorInput) element);
356: } catch (CoreException x) {
357: handleCoreException(
358: x,
359: TextEditorMessages.StorageDocumentProvider_isReadOnly);
360: }
361: }
362: return info.fIsReadOnly;
363: }
364: }
365: return super .isReadOnly(element);
366: }
367:
368: /*
369: * @see org.eclipse.ui.texteditor.IDocumentProviderExtension5#isNotSynchronizedException(Object, CoreException)
370: * @since 3.2
371: */
372: public boolean isNotSynchronizedException(Object element,
373: CoreException ex) {
374: IStatus status = ex.getStatus();
375: if (status == null || status instanceof MultiStatus)
376: return false;
377:
378: if (status.getException() != null)
379: return false;
380:
381: return status.getCode() == IResourceStatus.OUT_OF_SYNC_LOCAL;
382: }
383:
384: /*
385: * @see IDocumentProviderExtension#isModifiable(Object)
386: * @since 2.0
387: */
388: public boolean isModifiable(Object element) {
389: if (element instanceof IStorageEditorInput) {
390: StorageInfo info = (StorageInfo) getElementInfo(element);
391: if (info != null) {
392: if (info.fUpdateCache) {
393: try {
394: updateCache((IStorageEditorInput) element);
395: } catch (CoreException x) {
396: handleCoreException(
397: x,
398: TextEditorMessages.StorageDocumentProvider_isModifiable);
399: }
400: }
401: return info.fIsModifiable;
402: }
403: }
404: return super .isModifiable(element);
405: }
406:
407: /*
408: * @see AbstractDocumentProvider#doUpdateStateCache(Object)
409: * @since 2.0
410: */
411: protected void doUpdateStateCache(Object element)
412: throws CoreException {
413: if (element instanceof IStorageEditorInput) {
414: StorageInfo info = (StorageInfo) getElementInfo(element);
415: if (info != null)
416: info.fUpdateCache = true;
417: }
418: super .doUpdateStateCache(element);
419: }
420:
421: /*
422: * @see IStorageDocumentProvider#getDefaultEncoding()
423: * @since 2.0
424: */
425: public String getDefaultEncoding() {
426: return ResourcesPlugin.getEncoding();
427: }
428:
429: /*
430: * @see IStorageDocumentProvider#getEncoding(Object)
431: * @since 2.0
432: */
433: public String getEncoding(Object element) {
434: if (element instanceof IStorageEditorInput) {
435: StorageInfo info = (StorageInfo) getElementInfo(element);
436: if (info != null)
437: return info.fEncoding;
438: return getPersistedEncoding(element);
439: }
440: return null;
441: }
442:
443: /*
444: * @see IStorageDocumentProvider#setEncoding(Object, String)
445: * @since 2.0
446: */
447: public void setEncoding(Object element, String encoding) {
448: if (element instanceof IStorageEditorInput) {
449: StorageInfo info = (StorageInfo) getElementInfo(element);
450: if (info != null) {
451: info.fEncoding = encoding;
452: try {
453: persistEncoding(element, encoding);
454: } catch (CoreException ex) {
455: EditorsPlugin.log(ex.getStatus());
456: }
457: }
458: }
459: }
460:
461: /*
462: * @see org.eclipse.ui.texteditor.IDocumentProviderExtension4#getContentType(java.lang.Object)
463: * @since 3.1
464: */
465: public IContentType getContentType(Object element)
466: throws CoreException {
467: if (element instanceof IStorageEditorInput) {
468: IStorage storage = ((IStorageEditorInput) element)
469: .getStorage();
470: Reader reader = null;
471: InputStream stream = null;
472: try {
473: IContentDescription desc;
474: IDocument document = getDocument(element);
475: if (document != null) {
476: reader = new DocumentReader(document);
477: desc = Platform.getContentTypeManager()
478: .getDescriptionFor(reader,
479: storage.getName(), NO_PROPERTIES);
480: } else {
481: stream = storage.getContents();
482: desc = Platform.getContentTypeManager()
483: .getDescriptionFor(stream,
484: storage.getName(), NO_PROPERTIES);
485: }
486: if (desc != null && desc.getContentType() != null)
487: return desc.getContentType();
488: } catch (IOException x) {
489: IPath path = storage.getFullPath();
490: String name;
491: if (path != null)
492: name = path.toOSString();
493: else
494: name = storage.getName();
495: String message;
496: if (name != null)
497: message = NLSUtility
498: .format(
499: TextEditorMessages.StorageDocumentProvider_getContentDescriptionFor,
500: name);
501: else
502: message = TextEditorMessages.StorageDocumentProvider_getContentDescription;
503: throw new CoreException(new Status(IStatus.ERROR,
504: EditorsUI.PLUGIN_ID, IStatus.OK, message, x));
505: } finally {
506: try {
507: // Note: either 'reader' or 'stream' is null
508: if (reader != null)
509: reader.close();
510: if (stream != null)
511: stream.close();
512: } catch (IOException x) {
513: }
514: }
515: }
516: return super .getContentType(element);
517: }
518:
519: /**
520: * Returns the persisted encoding for the given element.
521: *
522: * @param element the element for which to get the persisted encoding
523: * @return the persisted encoding
524: * @since 2.1
525: */
526: protected String getPersistedEncoding(Object element) {
527: if (element instanceof IStorageEditorInput) {
528: IStorage storage;
529: try {
530: storage = ((IStorageEditorInput) element).getStorage();
531: if (storage instanceof IEncodedStorage)
532: return ((IEncodedStorage) storage).getCharset();
533: } catch (CoreException e) {
534: return null;
535: }
536: }
537: return null;
538: }
539:
540: /**
541: * Persists the given encoding for the given element.
542: *
543: * @param element the element for which to store the persisted encoding
544: * @param encoding the encoding
545: * @throws CoreException if the operation fails
546: * @since 2.1
547: */
548: protected void persistEncoding(Object element, String encoding)
549: throws CoreException {
550: // Default is to do nothing
551: }
552:
553: /*
554: * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#getOperationRunner(org.eclipse.core.runtime.IProgressMonitor)
555: * @since 3.0
556: */
557: protected IRunnableContext getOperationRunner(
558: IProgressMonitor monitor) {
559: return null;
560: }
561: }
|