/**
* Copyright (c) 2009, 2010 Mark Feber, MulgaSoft
*
* 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
*
*/
package com.mulgasoft.emacsplus;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.texteditor.ITextEditor;
/**
* Teco Register - Emulates Emacs' emulation of TECO's register feature
*
* From register.el:
* "This package of functions emulates and somewhat extends the <b>venerable</b>
* <b><i>TECO's</b></i> `register' feature, which permits you to save various useful
* pieces of buffer state to named variables."
*
* @author Mark Feber - initial API and implementation
*/
public class TecoRegister {
public static final String NAME = EmacsPlusActivator.getResourceString("Register_View_Name"); //$NON-NLS-1$
public static final String POINT = EmacsPlusActivator.getResourceString("Register_Contents_Point"); //$NON-NLS-1$
public static final String NUMBER = EmacsPlusActivator.getResourceString("Register_Contents_Number"); //$NON-NLS-1$
public static final String TEXT = EmacsPlusActivator.getResourceString("Register_Contents_Text"); //$NON-NLS-1$
public static final String RECTANGLE = EmacsPlusActivator.getResourceString("Register_Contents_Rectangle"); //$NON-NLS-1$
private final static String CR = "\n"; //$NON-NLS-1$
private static Map<String,IRegisterContents> register = new HashMap<String,IRegisterContents>();
private static TecoRegister instance = null;
private TecoRegister() {}
/**
* TecoRegister is a singleton
*
* @return global register instance
*/
public static TecoRegister getInstance() {
if (instance == null) {
instance = new TecoRegister();
}
return instance;
}
public Iterator<String> iterator() {
TreeSet<String> tset = new TreeSet<String>(register.keySet());
return tset.iterator();
}
/**
* Insert text for register
*
* @param key - register id
* @param text - new text
*/
public void put(String key,String text) {
if (text != null) {
RegisterContents contents = (RegisterContents)register.get(key);
if (contents != null) {
contents.setText(text);
} else {
register.put(key, new RegisterContents(text));
}
}
}
/**
* Get the text associated with this register
* If text is null, then if a number is present return it as a string
*
* @param key - register id
* @return the text, or null if no register or text
*/
public String getText(String key) {
String result = null;
IRegisterContents contents = register.get(key);
if (contents != null) {
result = contents.getText();
if (result == null) {
Integer i = contents.getNumber();
if (i != null) {
result = i.toString();
}
}
}
return result;
}
/**
* Insert rectangle into register
*
* @param key - register id
* @param rectangle - new rectangle
*/
public void put(String key,String[] rectangle) {
if (rectangle != null) {
RegisterContents contents = (RegisterContents)register.get(key);
if (contents != null) {
contents.setRectangle(rectangle);
} else {
register.put(key, new RegisterContents(rectangle));
}
}
}
/**
* Get the rectangle associated with this register
*
* @param key - register id
* @return the rectangle, or null if no register or not rectangle
*/
public String[] getRectangle(String key) {
String[] result = null;
IRegisterContents contents = register.get(key);
if (contents != null) {
result = contents.getRectangle();
if (result == null) {
}
}
return result;
}
/**
* Insert number into register
*
* @param key - register id
* @param number new number
*/
public void put(String key,Integer number) {
if (number != null) {
RegisterContents contents = (RegisterContents)register.get(key);
if (contents != null) {
contents.setNumber(number);
} else {
register.put(key, new RegisterContents(number));
}
}
}
/**
* Insert number into register
*
* @param key - register id
* @param number new number
*/
public void put(String key,int number) {
put(key, new Integer(number));
}
/**
* Get the number from the register
*
* @param key - register id
* @return the number or null if no register or number
*/
public Integer getNumber(String key) {
Integer result = null;
IRegisterContents contents = register.get(key);
if (contents != null) {
result = contents.getNumber();
}
return result;
}
public void put(String key,ITextEditor editor, int offset) {
RegisterContents contents = clearLocation(key);
if (contents != null) {
IRegisterLocation loc = contents.getLocation();
if (loc != null) {
((RegisterLocation)loc).setPosition(editor,offset);
} else {
contents.setLocation(new RegisterLocation(editor,offset));
}
} else {
contents = new RegisterContents(editor,offset);
register.put(key, contents);
}
}
/**
* Insert the position in the register
*
* @param key - register id
* @param path - the resource path
* @param offset - the offset in the part
*/
public void put(String key, IFile path, int offset) {
RegisterContents contents = clearLocation(key);
if (contents != null) {
IRegisterLocation loc = contents.getLocation();
if (loc != null) {
loc.setPath(path);
loc.setOffset(offset);
} else {
contents.setLocation(new RegisterLocation(path,offset));
}
} else {
contents = new RegisterContents(path,offset);
register.put(key, contents);
}
}
/**
* Get the location from the register
*
* @param key - register id
* @return the location or null if no register or location
*/
public IRegisterLocation getLocation(String key) {
IRegisterLocation result = null;
IRegisterContents contents = register.get(key);
if (contents != null) {
result = contents.getLocation();
}
return result;
}
/**
* Increment the number in the register by one
*
* @param key - register id
* @return the new number, or null if register did not contain a number
*/
public Integer increment(String key, int incr) {
Integer number = getNumber(key);
if (number != null) {
number = new Integer(number + incr);
put(key,number);
}
return number;
}
/**
* Get the raw contents of the register
*
* @param key - register id
* @return and IRegisterContents object or null
*/
public IRegisterContents getContents(String key) {
return register.get(key);
}
private IFile convertToPath(IEditorPart editor) {
IFile path = null;
if (editor != null && editor instanceof IEditorPart) {
IEditorInput input = ((IEditorPart) editor).getEditorInput();
if (input instanceof IFileEditorInput) {
path = ((IFileEditorInput) input).getFile();
}
}
return path;
}
private RegisterContents clearLocation(String key) {
// make sure the part and any listeners are removed when location is changed
IRegisterContents contents = register.get(key);
if (contents != null) {
if (contents.getLocation() != null) {
contents.getLocation().setPosition(null,null);
}
}
return (RegisterContents)contents;
}
/**
* inner class to hold the register contents
* Can hold a location and either text or a number
*/
private class RegisterContents implements IRegisterContents {
private Integer number = null;
private String text = null;
private String[] rectangle = null;
private IRegisterLocation location;
public RegisterContents(String text) {
this.text = text;
}
public RegisterContents(String[] rectangle) {
this.rectangle = rectangle;
}
public RegisterContents(Integer number) {
this.number = number;
}
public RegisterContents(ITextEditor editor, int offset) {
this.location = new RegisterLocation(editor,offset);
}
public RegisterContents(IFile path, int offset) {
this.location = new RegisterLocation(path,offset);
}
/**
* @see com.mulgasoft.emacsplus.IRegisterContents#getNumber()
*/
public Integer getNumber() {
return number;
}
void setNumber(Integer number) {
if (number != null) {
setText(null);
setRectangle(null);
}
this.number = number;
}
/**
* @see com.mulgasoft.emacsplus.IRegisterContents#getText()
*/
public String getText() {
return text;
}
void setText(String text) {
if (text != null) {
setNumber(null);
setRectangle(null);
}
this.text = text;
}
public String[] getRectangle() {
return rectangle;
}
void setRectangle(String[] rectangle) {
if (rectangle != null) {
setNumber(null);
setText(null);
}
this.rectangle = rectangle;
}
public IRegisterLocation getLocation() {
return location;
}
void setLocation(IRegisterLocation location) {
this.location= location;
}
public String toString() {
StringBuilder results = new StringBuilder();
results.append(CR);
if (number != null) {
results.append('<');
results.append(NUMBER);
results.append('>');
results.append(' ');
results.append(number);
results.append(CR);
} else if (text != null) {
results.append('<');
results.append(TEXT);
results.append('>');
results.append(CR);
results.append('\"');
results.append(text);
results.append('\"');
results.append(CR);
} else if (rectangle != null) {
results.append('<');
results.append(RECTANGLE);
results.append('>');
results.append(CR);
for (String txt : rectangle) {
results.append('\"');
results.append(txt);
results.append('\"');
results.append(CR);
}
results.append(CR);
}
if (location != null) {
results.append('<');
results.append(POINT);
results.append('>');
results.append(' ');
results.append(location.toString());
results.append(CR);
}
return results.toString();
}
}
private class RegisterLocation implements IRegisterLocation {
private IFile path = null;
private ITextEditor editor = null;
private Position position = null;
private EditorListener listener = null;
RegisterLocation(ITextEditor editor, int offset) {
this.setPosition(editor,offset);
}
RegisterLocation(IFile path, int offset) {
setPath(path);
this.position = new Position(offset,0);
}
/**
* @see com.mulgasoft.emacsplus.IBufferLocation#getOffset()
*/
public int getOffset() {
return (position != null ? position.getOffset() : 0);
}
public ITextEditor getEditor() {
return editor;
}
/**
* @see com.mulgasoft.emacsplus.IBufferLocation#getPath()
*/
public IFile getPath() {
return path;
}
/**
* @see com.mulgasoft.emacsplus.IBufferLocation#setOffset(int)
*/
public void setOffset(int offset) {
if (position != null) {
position.setOffset(offset);
} else {
position = new Position(offset,0);
}
}
/**
* @see com.mulgasoft.emacsplus.IBufferLocation#setOffset(int)
*/
public void setPosition(ITextEditor editor,Position position) {
if (this.editor != null && this.position != null) {
// remove the old position from the document updater
removePosition(this.editor,this.position);
}
this.editor = editor;
this.position = position;
if (position != null && editor != null) {
// add it to the document updater
addPosition(editor,position);
}
}
public void setPosition(ITextEditor editor,int offset) {
this.setPosition(editor,new Position(offset,0));
}
public Position getPosition() {
return position;
}
private void addPosition(ITextEditor editor, Position position) {
addListener();
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
// add position to document
try {
if (!document.containsPositionCategory(MarkRing.MARK_CATEGORY)) {
document.addPositionCategory(MarkRing.MARK_CATEGORY);
document.addPositionUpdater(MarkRing.updater);
}
document.addPosition(MarkRing.MARK_CATEGORY, position);
} catch (BadLocationException e) {
} catch (BadPositionCategoryException e) {
}
}
private void removePosition(ITextEditor editor, Position position) {
removeListener();
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
try {
document.removePosition(MarkRing.MARK_CATEGORY,position);
} catch (BadPositionCategoryException e) {
}
}
/**
* @see com.mulgasoft.emacsplus.IBufferLocation#setPath(java.lang.String)
*/
public void setPath(IFile path) {
this.path = path;
if (this.path != null) {
setEditor(null);
}
}
/**
* @see com.mulgasoft.emacsplus.IBufferLocation#setEditor(org.eclipse.ui.texteditor.ITextEditor)
*/
public void setEditor(ITextEditor editor) {
removeListener();
this.editor = editor;
if (this.editor != null) {
setPath(null);
addListener();
}
}
private void addListener() {
listener = new EditorListener(this);
listener.addListener(listener);
}
private void removeListener() {
if (listener != null) {
listener.removeListener(listener);
clearListener();
}
}
void clearListener() {
listener = null;
}
public String toString() {
String result = null;
if (getPosition() != null) {
if (getEditor() != null) {
result = getEditor().getTitle() + ' ' + getPosition().getOffset();
} else if (getPath() != null) {
result = getPath().getFullPath().toString() + ' ' + getPosition().getOffset();
}
}
return result;
}
}
// And now for the listener
private class EditorListener implements IPartListener2 {
RegisterLocation location = null;
EditorListener(RegisterLocation location) {
this.location = location;
}
public void removeListener(IPartListener2 listener) {
IWorkbenchPage page = EmacsPlusUtils.getWorkbenchPage();
if (page != null && location != null) {
page.removePartListener(listener);
}
location.clearListener();
location = null;
}
public void addListener(IPartListener2 listener) {
IWorkbenchPage page = EmacsPlusUtils.getWorkbenchPage();
if (page != null) {
page.addPartListener(listener);
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
*/
public void partClosed(IWorkbenchPartReference partRef) {
if (partRef instanceof IEditorReference) {
IEditorPart epart = ((IEditorReference) partRef).getEditor(false);
ITextEditor editor = (location != null ? location.getEditor() : null);
if (editor == EmacsPlusUtils.getTextEditor(epart, false)) {
RegisterLocation loc = location;
// we're out of here
removeListener(this);
IDocument document = editor.getDocumentProvider().getDocument(editor.getEditorInput());
// remove position category, if still present
if (document.containsPositionCategory(MarkRing.MARK_CATEGORY)) {
try {
document.removePositionUpdater(MarkRing.updater);
document.removePositionCategory(MarkRing.MARK_CATEGORY);
} catch (BadPositionCategoryException e) {
}
}
// convert to path
loc.setPath(convertToPath(editor));
}
}
}
public void partActivated(IWorkbenchPartReference partRef) {}
public void partBroughtToTop(IWorkbenchPartReference partRef) {}
public void partDeactivated(IWorkbenchPartReference partRef) {}
public void partHidden(IWorkbenchPartReference partRef) {}
public void partInputChanged(IWorkbenchPartReference partRef) {}
public void partOpened(IWorkbenchPartReference partRef) {}
public void partVisible(IWorkbenchPartReference partRef) {}
}
}