org.eclipse.che.jdt.internal.core.Buffer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.che.jdt.internal.core.Buffer.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2012 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.che.jdt.internal.core;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.jdt.core.BufferChangedEvent;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IBufferChangedListener;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.util.Util;

import java.util.ArrayList;

/**
 * @see org.eclipse.jdt.core.IBuffer
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Buffer implements IBuffer {
    protected IFile file;
    protected int flags;
    protected char[] contents;
    protected ArrayList changeListeners;
    protected IOpenable owner;
    protected int gapStart = -1;
    protected int gapEnd = -1;

    protected Object lock = new Object();

    protected static final int F_HAS_UNSAVED_CHANGES = 1;
    protected static final int F_IS_READ_ONLY = 2;
    protected static final int F_IS_CLOSED = 4;

    /**
     * Creates a new buffer on an underlying resource.
     */
    protected Buffer(IFile file, IOpenable owner, boolean readOnly) {
        this.file = file;
        this.owner = owner;
        if (file == null) {
            setReadOnly(readOnly);
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public synchronized void addBufferChangedListener(IBufferChangedListener listener) {
        if (this.changeListeners == null) {
            this.changeListeners = new ArrayList(5);
        }
        if (!this.changeListeners.contains(listener)) {
            this.changeListeners.add(listener);
        }
    }

    /**
     * Append the <code>text</code> to the actual content, the gap is moved
     * to the end of the <code>text</code>.
     */
    public void append(char[] text) {
        if (!isReadOnly()) {
            if (text == null || text.length == 0) {
                return;
            }
            int length = getLength();
            synchronized (this.lock) {
                if (this.contents == null)
                    return;
                moveAndResizeGap(length, text.length);
                System.arraycopy(text, 0, this.contents, length, text.length);
                this.gapStart += text.length;
                this.flags |= F_HAS_UNSAVED_CHANGES;
            }
            notifyChanged(new BufferChangedEvent(this, length, 0, new String(text)));
        }
    }

    /**
     * Append the <code>text</code> to the actual content, the gap is moved
     * to the end of the <code>text</code>.
     */
    public void append(String text) {
        if (text == null) {
            return;
        }
        this.append(text.toCharArray());
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public void close() {
        BufferChangedEvent event = null;
        synchronized (this.lock) {
            if (isClosed())
                return;
            event = new BufferChangedEvent(this, 0, 0, null);
            this.contents = null;
            this.flags |= F_IS_CLOSED;
        }
        notifyChanged(event); // notify outside of synchronized block
        synchronized (this) { // ensure that no other thread is adding/removing a listener at the same time (https://bugs.eclipse.org/bugs/show_bug.cgi?id=126673)
            this.changeListeners = null;
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public char getChar(int position) {
        synchronized (this.lock) {
            if (this.contents == null)
                return Character.MIN_VALUE;
            if (position < this.gapStart) {
                return this.contents[position];
            }
            int gapLength = this.gapEnd - this.gapStart;
            return this.contents[position + gapLength];
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public char[] getCharacters() {
        synchronized (this.lock) {
            if (this.contents == null)
                return null;
            if (this.gapStart < 0) {
                return this.contents;
            }
            int length = this.contents.length;
            char[] newContents = new char[length - this.gapEnd + this.gapStart];
            System.arraycopy(this.contents, 0, newContents, 0, this.gapStart);
            System.arraycopy(this.contents, this.gapEnd, newContents, this.gapStart, length - this.gapEnd);
            return newContents;
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public String getContents() {
        char[] chars = getCharacters();
        if (chars == null)
            return null;
        return new String(chars);
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public int getLength() {
        synchronized (this.lock) {
            if (this.contents == null)
                return -1;
            int length = this.gapEnd - this.gapStart;
            return (this.contents.length - length);
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public IOpenable getOwner() {
        return this.owner;
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public String getText(int offset, int length) {
        synchronized (this.lock) {
            if (this.contents == null)
                return ""; //$NON-NLS-1$
            if (offset + length < this.gapStart)
                return new String(this.contents, offset, length);
            if (this.gapStart < offset) {
                int gapLength = this.gapEnd - this.gapStart;
                return new String(this.contents, offset + gapLength, length);
            }
            StringBuffer buf = new StringBuffer();
            buf.append(this.contents, offset, this.gapStart - offset);
            buf.append(this.contents, this.gapEnd, offset + length - this.gapStart);
            return buf.toString();
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public IResource getUnderlyingResource() {
        return this.file;
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public boolean hasUnsavedChanges() {
        return (this.flags & F_HAS_UNSAVED_CHANGES) != 0;
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public boolean isClosed() {
        return (this.flags & F_IS_CLOSED) != 0;
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public boolean isReadOnly() {
        return (this.flags & F_IS_READ_ONLY) != 0;
    }

    /**
     * Moves the gap to location and adjust its size to the
     * anticipated change size. The size represents the expected
     * range of the gap that will be filled after the gap has been moved.
     * Thus the gap is resized to actual size + the specified size and
     * moved to the given position.
     */
    protected void moveAndResizeGap(int position, int size) {
        char[] content = null;
        int oldSize = this.gapEnd - this.gapStart;
        if (size < 0) {
            if (oldSize > 0) {
                content = new char[this.contents.length - oldSize];
                System.arraycopy(this.contents, 0, content, 0, this.gapStart);
                System.arraycopy(this.contents, this.gapEnd, content, this.gapStart,
                        content.length - this.gapStart);
                this.contents = content;
            }
            this.gapStart = this.gapEnd = position;
            return;
        }
        content = new char[this.contents.length + (size - oldSize)];
        int newGapStart = position;
        int newGapEnd = newGapStart + size;
        if (oldSize == 0) {
            System.arraycopy(this.contents, 0, content, 0, newGapStart);
            System.arraycopy(this.contents, newGapStart, content, newGapEnd, content.length - newGapEnd);
        } else if (newGapStart < this.gapStart) {
            int delta = this.gapStart - newGapStart;
            System.arraycopy(this.contents, 0, content, 0, newGapStart);
            System.arraycopy(this.contents, newGapStart, content, newGapEnd, delta);
            System.arraycopy(this.contents, this.gapEnd, content, newGapEnd + delta,
                    this.contents.length - this.gapEnd);
        } else {
            int delta = newGapStart - this.gapStart;
            System.arraycopy(this.contents, 0, content, 0, this.gapStart);
            System.arraycopy(this.contents, this.gapEnd, content, this.gapStart, delta);
            System.arraycopy(this.contents, this.gapEnd + delta, content, newGapEnd, content.length - newGapEnd);
        }
        this.contents = content;
        this.gapStart = newGapStart;
        this.gapEnd = newGapEnd;
    }

    /**
     * Notify the listeners that this buffer has changed.
     * To avoid deadlock, this should not be called in a synchronized block.
     */
    protected void notifyChanged(final BufferChangedEvent event) {
        ArrayList listeners = this.changeListeners;
        if (listeners != null) {
            for (int i = 0, size = listeners.size(); i < size; ++i) {
                final IBufferChangedListener listener = (IBufferChangedListener) listeners.get(i);
                SafeRunner.run(new ISafeRunnable() {
                    public void handleException(Throwable exception) {
                        Util.log(exception, "Exception occurred in listener of buffer change notification"); //$NON-NLS-1$
                    }

                    public void run() throws Exception {
                        listener.bufferChanged(event);
                    }
                });

            }
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public synchronized void removeBufferChangedListener(IBufferChangedListener listener) {
        if (this.changeListeners != null) {
            this.changeListeners.remove(listener);
            if (this.changeListeners.size() == 0) {
                this.changeListeners = null;
            }
        }
    }

    /**
     * Replaces <code>length</code> characters starting from <code>position</code> with <code>text<code>.
     * After that operation, the gap is placed at the end of the
     * inserted <code>text</code>.
     */
    public void replace(int position, int length, char[] text) {
        if (!isReadOnly()) {
            int textLength = text == null ? 0 : text.length;
            synchronized (this.lock) {
                if (this.contents == null)
                    return;

                // move gap
                moveAndResizeGap(position + length, textLength - length);

                // overwrite
                int min = Math.min(textLength, length);
                if (min > 0) {
                    System.arraycopy(text, 0, this.contents, position, min);
                }
                if (length > textLength) {
                    // enlarge the gap
                    this.gapStart -= length - textLength;
                } else if (textLength > length) {
                    // shrink gap
                    this.gapStart += textLength - length;
                    System.arraycopy(text, 0, this.contents, position, textLength);
                }
                this.flags |= F_HAS_UNSAVED_CHANGES;
            }
            String string = null;
            if (textLength > 0) {
                string = new String(text);
            }
            notifyChanged(new BufferChangedEvent(this, position, length, string));
        }
    }

    /**
     * Replaces <code>length</code> characters starting from <code>position</code> with <code>text<code>.
     * After that operation, the gap is placed at the end of the
     * inserted <code>text</code>.
     */
    public void replace(int position, int length, String text) {
        this.replace(position, length, text == null ? null : text.toCharArray());
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public void save(IProgressMonitor progress, boolean force) throws JavaModelException {

        //   // determine if saving is required
        //   if (isReadOnly() || this.file == null) {
        //      return;
        //   }
        //   if (!hasUnsavedChanges())
        //      return;
        //
        //   // use a platform operation to update the resource contents
        //   try {
        //      String stringContents = getContents();
        //      if (stringContents == null) return;
        //
        //      // Get encoding
        //      String encoding = null;
        //      try {
        //         encoding = this.file.getCharset();
        //      }
        //      catch (CoreException ce) {
        //         // use no encoding
        //      }
        //
        //      // Create bytes array
        //      byte[] bytes = encoding == null
        //         ? stringContents.getBytes()
        //         : stringContents.getBytes(encoding);
        //
        //      // Special case for UTF-8 BOM files
        //      // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=110576
        //      if (encoding != null && encoding.equals(org.eclipse.jdt.internal.compiler.util.Util.UTF_8)) {
        //         IContentDescription description;
        //         try {
        //            description = this.file.getContentDescription();
        //         } catch (CoreException e) {
        //            if (e.getStatus().getCode() != IResourceStatus.RESOURCE_NOT_FOUND)
        //               throw e;
        //            // file no longer exist (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=234307 )
        //            description = null;
        //         }
        //         if (description != null && description.getProperty(IContentDescription.BYTE_ORDER_MARK) != null) {
        //            int bomLength= IContentDescription.BOM_UTF_8.length;
        //            byte[] bytesWithBOM= new byte[bytes.length + bomLength];
        //            System.arraycopy(IContentDescription.BOM_UTF_8, 0, bytesWithBOM, 0, bomLength);
        //            System.arraycopy(bytes, 0, bytesWithBOM, bomLength, bytes.length);
        //            bytes= bytesWithBOM;
        //         }
        //      }
        //
        //      // Set file contents
        //      ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
        //      if (this.file.exists()) {
        //         this.file.setContents(
        //            stream,
        //            force ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY,
        //            null);
        //      } else {
        //         this.file.create(stream, force, null);
        //      }
        //   } catch (IOException e) {
        //      throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
        //   } catch (CoreException e) {
        //      throw new JavaModelException(e);
        //   }
        //
        //   // the resource no longer has unsaved changes
        //   this.flags &= ~ (F_HAS_UNSAVED_CHANGES);
        throw new UnsupportedOperationException("Save is not implemented");
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public void setContents(char[] newContents) {
        // allow special case for first initialization
        // after creation by buffer factory
        if (this.contents == null) {
            synchronized (this.lock) {
                this.contents = newContents;
                this.flags &= ~(F_HAS_UNSAVED_CHANGES);
            }
            return;
        }

        if (!isReadOnly()) {
            String string = null;
            if (newContents != null) {
                string = new String(newContents);
            }
            synchronized (this.lock) {
                if (this.contents == null)
                    return; // ignore if buffer is closed (as per spec)
                this.contents = newContents;
                this.flags |= F_HAS_UNSAVED_CHANGES;
                this.gapStart = -1;
                this.gapEnd = -1;
            }
            BufferChangedEvent event = new BufferChangedEvent(this, 0, getLength(), string);
            notifyChanged(event);
        }
    }

    /**
     * @see org.eclipse.jdt.core.IBuffer
     */
    public void setContents(String newContents) {
        this.setContents(newContents.toCharArray());
    }

    /**
     * Sets this <code>Buffer</code> to be read only.
     */
    protected void setReadOnly(boolean readOnly) {
        if (readOnly) {
            this.flags |= F_IS_READ_ONLY;
        } else {
            this.flags &= ~(F_IS_READ_ONLY);
        }
    }

    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append("Owner: " + ((JavaElement) this.owner).toStringWithAncestors()); //$NON-NLS-1$
        buffer.append("\nHas unsaved changes: " + hasUnsavedChanges()); //$NON-NLS-1$
        buffer.append("\nIs readonly: " + isReadOnly()); //$NON-NLS-1$
        buffer.append("\nIs closed: " + isClosed()); //$NON-NLS-1$
        buffer.append("\nContents:\n"); //$NON-NLS-1$
        char[] charContents = getCharacters();
        if (charContents == null) {
            buffer.append("<null>"); //$NON-NLS-1$
        } else {
            int length = charContents.length;
            for (int i = 0; i < length; i++) {
                char c = charContents[i];
                switch (c) {
                case '\n':
                    buffer.append("\\n\n"); //$NON-NLS-1$
                    break;
                case '\r':
                    if (i < length - 1 && this.contents[i + 1] == '\n') {
                        buffer.append("\\r\\n\n"); //$NON-NLS-1$
                        i++;
                    } else {
                        buffer.append("\\r\n"); //$NON-NLS-1$
                    }
                    break;
                default:
                    buffer.append(c);
                    break;
                }
            }
        }
        return buffer.toString();
    }
}