/******************************************************************************* * Copyright (c) Gil Barash - chookapp@yahoo.com * 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: * Gil Barash - initial API and implementation *******************************************************************************/ package com.chookapp.org.bracketeer.core; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.DefaultPositionUpdater; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IPositionUpdater; import org.eclipse.jface.text.ISynchronizable; import org.eclipse.jface.text.Position; import org.eclipse.ui.services.IDisposable; import com.chookapp.org.bracketeer.Activator; import com.chookapp.org.bracketeer.common.BracketsPair; import com.chookapp.org.bracketeer.common.Hint; import com.chookapp.org.bracketeer.common.IBracketeerProcessingContainer; import com.chookapp.org.bracketeer.common.SingleBracket; public class BracketeerProcessingContainer implements IDisposable, IBracketeerProcessingContainer { private class ObjectContainer<T> { private T _object; private boolean _toDelete; public ObjectContainer(T obj) { _object = obj; } public T getObject() { return _object; } public boolean isToDelete() { return _toDelete; } public void setToDelete(boolean toDelete) { _toDelete = toDelete; } @Override public boolean equals(Object other) { if( other == null ) return false; if( other instanceof ObjectContainer<?> ) { return _object.equals(((ObjectContainer<?>) other)._object); } return _object.equals(other); } } private IDocument _doc; private Object _docLock; private List<ObjectContainer<SingleBracket>> _singleBrackets; private List<ObjectContainer<BracketsPair>> _bracketsPairList; private List<ObjectContainer<Hint>> _hints; private String _positionCategory; private IPositionUpdater _positionUpdater; private List<IProcessingContainerListener> _listeners; private boolean _bracketsPairsTouched; private boolean _singleBracketsTouched; private boolean _hintsTouched; private boolean _updatingListeners; public BracketeerProcessingContainer(IDocument doc) { _singleBrackets = new ArrayList<ObjectContainer<SingleBracket>>(); _bracketsPairList = new LinkedList<ObjectContainer<BracketsPair>>(); _hints = new LinkedList<ObjectContainer<Hint>>(); _doc = doc; if( _doc instanceof ISynchronizable ) _docLock = ((ISynchronizable) _doc).getLockObject(); else _docLock = new Object(); _positionCategory = "bracketeerPosition"; //$NON-NLS-1$ _doc.addPositionCategory(_positionCategory); _positionUpdater = new DefaultPositionUpdater(_positionCategory); _doc.addPositionUpdater(_positionUpdater); _listeners = new LinkedList<IProcessingContainerListener>(); _updatingListeners = false; } @Override public void dispose() { _doc.removePositionUpdater(_positionUpdater); try { _doc.removePositionCategory(_positionCategory); } catch (BadPositionCategoryException e) { Activator.log(e); } } public void addListener(IProcessingContainerListener listener) { _listeners.add(listener); } public void removeListener(IProcessingContainerListener listener) { if( !_listeners.remove(listener) ) Activator.log(Messages.BracketeerProcessingContainer_listsnerNotFound); } public BracketsPair getMatchingPair(int openOffset, int closeOffset) { synchronized(_docLock) { for (ObjectContainer<BracketsPair> objCont : _bracketsPairList) { if(objCont.isToDelete()) continue; BracketsPair pair = objCont.getObject(); Position openPos = pair.getOpeningBracket().getPosition(); if(openPos != null && openPos.getOffset() == openOffset) { Position closePos = pair.getClosingBracket().getPosition(); if(closePos != null && closePos.getOffset() == closeOffset ) return pair; Activator.log(String.format("[%1$d,%2$d] paritally found - %3$s", //$NON-NLS-1$ openOffset, closeOffset, pair.toString())); return null; } } } return null; } public List<BracketsPair> getPairsSurrounding(int offset) { List<BracketsPair> retVal = new LinkedList<BracketsPair>(); synchronized(_docLock) { for (ObjectContainer<BracketsPair> objCont : _bracketsPairList) { BracketsPair pair = objCont.getObject(); Position opBrPos = pair.getOpeningBracket().getPosition(); Position clBrPos = pair.getClosingBracket().getPosition(); if( (opBrPos == null) || (clBrPos == null) ) continue; if( (opBrPos.offset <= offset) && (clBrPos.offset > offset) ) { if( !retVal.contains(pair) ) { retVal.add(pair); } } } } return retVal; } public List<BracketsPair> getMatchingPairs(int startOffset, int length) { List<BracketsPair> retVal = new LinkedList<BracketsPair>(); synchronized(_docLock) { for (ObjectContainer<BracketsPair> objCont : _bracketsPairList) { BracketsPair pair = objCont.getObject(); for (SingleBracket br : pair.getBrackets()) { Position pos = br.getPosition(); if(pos != null && pos.overlapsWith(startOffset, length) && !retVal.contains(pair) ) { retVal.add(pair); break; } } } } return retVal; } public List<SingleBracket> getSingleBrackets() { List<SingleBracket> ret = new LinkedList<SingleBracket>(); synchronized(_docLock) { for (ObjectContainer<SingleBracket> objCont : _singleBrackets) { SingleBracket br = objCont.getObject(); if( br.getPosition() != null ) ret.add(br); } } return ret; } public Hint getHint(int startOffset) { synchronized(_docLock) { for (ObjectContainer<Hint> objCont : _hints) { Hint hint = objCont.getObject(); Position pos = hint.getHintPosition(); if( pos != null && pos.overlapsWith(startOffset, 1) ) return hint; } } return null; } public List<Hint> getHints() { List<Hint> ret = new LinkedList<Hint>(); synchronized(_docLock) { for (ObjectContainer<Hint> objCont : _hints) { Hint hint = objCont.getObject(); if( !hint.hasDeletedPosition() ) ret.add(hint); } } return ret; } public List<BracketsPair> getBracketPairs() { List<BracketsPair> ret = new LinkedList<BracketsPair>(); synchronized(_docLock) { for (ObjectContainer<BracketsPair> objCont : _bracketsPairList) { BracketsPair pair = objCont.getObject(); if( !pair.hasDeletedPosition() ) ret.add(pair); } } return ret; } public void markAllToBeDeleted() { synchronized(_docLock) { for (ObjectContainer<BracketsPair> objCont : _bracketsPairList) { objCont.setToDelete(true); } for (ObjectContainer<SingleBracket> objCont : _singleBrackets) { objCont.setToDelete(true); } for (ObjectContainer<Hint> objCont : _hints) { objCont.setToDelete(true); } } } public void deleteAllMarked() { synchronized(_docLock) { { Iterator<ObjectContainer<BracketsPair>> it = _bracketsPairList.iterator(); while(it.hasNext()) { ObjectContainer<BracketsPair> objCont = it.next(); if( objCont.isToDelete() ) { _bracketsPairsTouched = true; for (SingleBracket bracket : objCont.getObject().getBrackets()) { delete(bracket.getPositionRaw()); } it.remove(); } } } { Iterator<ObjectContainer<SingleBracket>> it = _singleBrackets.iterator(); while(it.hasNext()) { ObjectContainer<SingleBracket> objCont = it.next(); if( objCont.isToDelete() ) { _singleBracketsTouched = true; delete(objCont.getObject().getPositionRaw()); it.remove(); } } } { Iterator<ObjectContainer<Hint>> it = _hints.iterator(); while(it.hasNext()) { ObjectContainer<Hint> objCont = it.next(); if( objCont.isToDelete() ) { _hintsTouched = true; delete(objCont.getObject().getOriginPositionRaw()); delete(objCont.getObject().getHintPositionRaw()); it.remove(); } } } } if(Activator.DEBUG) { try { Activator.trace("Positions tracked = " + _doc.getPositions(_positionCategory).length); //$NON-NLS-1$ } catch (BadPositionCategoryException e) { Activator.log(e); } Activator.trace("Pairs = " + _bracketsPairList.size()); //$NON-NLS-1$ Activator.trace("Singles = " + _singleBrackets.size()); //$NON-NLS-1$ Activator.trace("Hints = " + _hints.size()); //$NON-NLS-1$ } } public void updateComplete() { _updatingListeners = true; for (IProcessingContainerListener listener : _listeners) { listener.containerUpdated(_bracketsPairsTouched, _singleBracketsTouched, _hintsTouched); } _bracketsPairsTouched = false; _singleBracketsTouched = false; _hintsTouched = false; _updatingListeners = false; } public boolean isUpdatingListeners() { return _updatingListeners; } public void add(BracketsPair pair) throws BadLocationException { synchronized(_docLock) { ObjectContainer<BracketsPair> existing = findExistingObj(_bracketsPairList, pair); if( existing != null ) { if( existing.equals(pair) && !existing.getObject().hasDeletedPosition() ) { existing.setToDelete(false); return; } else { deletePair(existing); } } _bracketsPairsTouched = true; ObjectContainer<BracketsPair> pairContainer = new ObjectContainer<BracketsPair>(pair); _bracketsPairList.add(pairContainer); for (SingleBracket br : pair.getBrackets()) { addPosition(br.getPosition()); } } } public void add(SingleBracket bracket) throws BadLocationException { synchronized(_docLock) { ObjectContainer<SingleBracket> existing = findExistingObj(_singleBrackets, bracket); if( existing != null ) { if( existing.equals(bracket) && existing.getObject().getPosition() != null ) { existing.setToDelete(false); return; } else { deleteSingle(existing); } } _singleBracketsTouched = true; _singleBrackets.add(new ObjectContainer<SingleBracket>(bracket)); addPosition(bracket.getPosition()); } } public void add(Hint hint) throws BadLocationException { synchronized(_docLock) { ObjectContainer<Hint> existing = findExistingObj(_hints, hint); if( existing != null ) { if( existing.equals(hint) && !existing.getObject().hasDeletedPosition() ) { existing.setToDelete(false); return; } else { deleteHint(existing); } } _hintsTouched = true; _hints.add(new ObjectContainer<Hint>(hint)); addPosition(hint.getHintPositionRaw()); addPosition(hint.getOriginPositionRaw()); } } private void addPosition(Position position) throws BadLocationException { try { if( position != null ) _doc.addPosition(_positionCategory, position); } catch (BadPositionCategoryException e) { Activator.log(e); } } private static <T> ObjectContainer<T> findExistingObj(List<ObjectContainer<T>> objList, T obj) { for (ObjectContainer<T> objCont : objList) { if(objCont.equals(obj)) return objCont; } return null; } private void delete(Position position) { try { _doc.removePosition(_positionCategory, position); } catch (BadPositionCategoryException e) { Activator.log(e); } } private void deletePair(ObjectContainer<BracketsPair> objCont) { synchronized(_docLock) { boolean found = _bracketsPairList.remove(objCont); Assert.isTrue(found); for (SingleBracket bracket : objCont.getObject().getBrackets()) { delete(bracket.getPositionRaw()); } } } private void deleteSingle(ObjectContainer<SingleBracket> objCont) { synchronized(_docLock) { boolean found = _singleBrackets.remove(objCont); Assert.isTrue(found); SingleBracket bracket = objCont.getObject(); delete(bracket.getPositionRaw()); } } private void deleteHint(ObjectContainer<Hint> objCont) { synchronized(_docLock) { boolean found = _hints.remove(objCont); Assert.isTrue(found); Hint hint = objCont.getObject(); delete(hint.getOriginPositionRaw()); delete(hint.getHintPositionRaw()); } } /* private static <T> List<T> mapObjListToObjList(Collection<ObjectContainer<T>> vals) { List<T> retVal = new LinkedList<T>(); for (ObjectContainer<T> mapObj : vals) { if( !retVal.contains(mapObj.getObject()) && !mapObj.isToDelete() ) retVal.add(mapObj.getObject()); } return retVal; } */ }