| /******************************************************************************* |
| * Copyright (c) 2004, 2011 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.wst.css.ui.internal.contentassist; |
| |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.wst.css.core.internal.metamodel.CSSMetaModel; |
| import org.eclipse.wst.css.core.internal.metamodel.util.CSSMetaModelFinder; |
| import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSDocument; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSModel; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSNode; |
| import org.eclipse.wst.css.core.internal.util.CSSUtil; |
| import org.eclipse.wst.css.core.internal.util.RegionIterator; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; |
| import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; |
| |
| class CSSContentAssistContext { |
| |
| private int fReplaceBegin = -1; |
| private String fTextToReplace = null; |
| private String fTextToCompare = null; |
| private int fTargetPos = -1; |
| private ICSSNode fTargetNode = null; |
| private int fCursorPos = -1; |
| private int fLength = -1; |
| private IStructuredDocument fStructuredDocument = null; |
| private int fDocumentOffset = 0; |
| private char fQuote = 0; |
| private ICSSModel fModel = null; |
| private boolean fSelected = false; |
| |
| /** |
| * |
| */ |
| private CSSContentAssistContext() { |
| super(); |
| } |
| |
| /** |
| * |
| */ |
| CSSContentAssistContext(int documentPosition, ICSSNode node, int documentOffset, char quote) { |
| this(); |
| fCursorPos = documentPosition; |
| fDocumentOffset = documentOffset; |
| fQuote = quote; |
| initialize(node.getOwnerDocument()); |
| } |
| |
| CSSContentAssistContext(int documentPosition, ICSSNode node, int documentOffset, int length, char quote) { |
| this(); |
| fCursorPos = documentPosition; |
| fDocumentOffset = documentOffset; |
| fQuote = quote; |
| fLength = length; |
| initialize(node.getOwnerDocument()); |
| } |
| |
| CSSContentAssistContext(int documentPosition, ICSSNode node, int documentOffset, char quote, boolean selected) { |
| this(); |
| fCursorPos = documentPosition; |
| fDocumentOffset = documentOffset; |
| fQuote = quote; |
| fSelected = selected; |
| initialize(node.getOwnerDocument()); |
| } |
| |
| /** |
| * @return int |
| */ |
| int getCursorPos() { |
| return fCursorPos; |
| } |
| |
| /** |
| * @return int |
| */ |
| int getDocumentOffset() { |
| return fDocumentOffset; |
| } |
| |
| IStructuredDocument getStructuredDocument() { |
| return fStructuredDocument; |
| } |
| |
| ICSSModel getModel() { |
| return fModel; |
| } |
| |
| private ICSSNode getNodeAt(int offset) { |
| return (ICSSNode) ((fModel != null) ? fModel.getIndexedRegion(offset) : null); |
| } |
| |
| /** |
| * |
| * @return char |
| */ |
| char getQuoteOfStyleAttribute() { |
| return fQuote; |
| } |
| |
| ITextRegion getRegionByOffset(int offset) { |
| ITextRegion region = null; |
| if (fStructuredDocument != null) { |
| IStructuredDocumentRegion flatNode = fStructuredDocument.getRegionAtCharacterOffset(offset); |
| if (flatNode != null) { |
| //if its a quoted region and at the beginning of a node then get the node before it |
| if(offset == flatNode.getStartOffset() && fQuote != 0) { |
| flatNode = fStructuredDocument.getRegionAtCharacterOffset(offset-1); |
| } |
| |
| //if its the end offset of the node then to get the region need to step back one |
| if(offset == flatNode.getEndOffset()) { |
| region = flatNode.getRegionAtCharacterOffset(offset-1); |
| } else { |
| region = flatNode.getRegionAtCharacterOffset(offset); |
| } |
| } |
| } |
| return region; |
| } |
| |
| /** |
| * |
| */ |
| // String getRegionText() { |
| // ITextRegion targetRegion = getTargetRegion(); |
| // if (targetRegion != null) { |
| // return targetRegion.getText(); |
| // } else { |
| // return ""; //$NON-NLS-1$ |
| // } |
| // } |
| /** |
| * |
| */ |
| int getReplaceBegin() { |
| return fReplaceBegin; |
| } |
| |
| ICSSNode getTargetNode() { |
| return fTargetNode; |
| } |
| |
| private int getTargetPos() { |
| return fTargetPos; |
| } |
| |
| ITextRegion getTargetRegion() { |
| return getRegionByOffset(getTargetPos()); |
| } |
| |
| private IStructuredDocumentRegion getTargetDocumentRegion() { |
| return getDocumentRegionByOffset(getTargetPos()); |
| } |
| |
| private IStructuredDocumentRegion getDocumentRegionByOffset(int offset) { |
| return (fStructuredDocument != null) ? fStructuredDocument.getRegionAtCharacterOffset(offset) : null; |
| } |
| |
| ITextRegion getTargetRegionPrevious() { |
| ITextRegion previousRegion = null; |
| ITextRegion targetRegion = getTargetRegion(); |
| RegionIterator iterator = null; |
| if (targetRegion == null) { |
| if (0 < fCursorPos) { |
| iterator = new RegionIterator(fStructuredDocument, fCursorPos - 1); |
| } |
| } else { |
| iterator = getRegionIterator(); |
| if (iterator.hasPrev()) { |
| iterator.prev(); |
| } else { |
| iterator = null; |
| } |
| } |
| if (iterator != null) { |
| while (iterator.hasPrev()) { |
| ITextRegion region = iterator.prev(); |
| String type = region.getType(); |
| if (type != CSSRegionContexts.CSS_S && type != CSSRegionContexts.CSS_COMMENT && type != CSSRegionContexts.CSS_CDO && type != CSSRegionContexts.CSS_CDC) { |
| previousRegion = region; |
| break; |
| } |
| } |
| } |
| |
| return previousRegion; |
| } |
| |
| /** |
| * @return java.lang.String |
| */ |
| String getTextToCompare() { |
| return fTextToCompare; |
| } |
| |
| /** |
| * |
| */ |
| String getTextToReplace() { |
| return fTextToReplace; |
| } |
| |
| /** |
| * |
| */ |
| private void initialize(ICSSDocument doc) { |
| if (doc == null) { |
| return; |
| } |
| ICSSModel model = doc.getModel(); |
| fModel = model; |
| fStructuredDocument = model.getStructuredDocument(); |
| |
| initializeTargetPos(); |
| initializeTargetText(); |
| initializeTargetNode(); |
| } |
| |
| /** |
| * |
| */ |
| private void initializeTargetNode() { |
| if (fCursorPos == 0) { |
| fTargetNode = fModel.getDocument(); |
| return; |
| } |
| |
| // find edge of tree node |
| ICSSNode cursorNode = getNodeAt(fCursorPos); |
| if (cursorNode == null) { // end of document |
| cursorNode = fModel.getDocument(); |
| } |
| ICSSNode node = null; |
| IStructuredDocumentRegion flatNode = fStructuredDocument.getRegionAtCharacterOffset(fCursorPos - 1); |
| while (flatNode != null && (node = getNodeAt(flatNode.getStartOffset())) == cursorNode && ((IndexedRegion) node).getStartOffset() != flatNode.getStartOffset()) { |
| flatNode = flatNode.getPrevious(); |
| } |
| if (flatNode == null) { // top of document |
| fTargetNode = (node == null) ? fModel.getDocument() : node; |
| return; |
| } |
| // v<--| |
| // AAAAAA |
| // BBBBBBBBBB cursorNode:A , node:B -> target is A |
| if (cursorNode != null) { |
| for (ICSSNode parent = cursorNode.getParentNode(); parent != null; parent = parent.getParentNode()) { |
| if (parent == cursorNode) { |
| fTargetNode = cursorNode; |
| return; |
| } |
| } |
| } |
| // v<--| |
| // AAA |
| // BBBBBBBBBB cursorNode:B , node:A -> depend on A's node type |
| short nodeType = node.getNodeType(); |
| if (nodeType == ICSSNode.STYLEDECLITEM_NODE || nodeType == ICSSNode.CHARSETRULE_NODE || nodeType == ICSSNode.IMPORTRULE_NODE) { |
| String type = CSSUtil.getStructuredDocumentRegionType(flatNode); |
| if (type == CSSRegionContexts.CSS_DELIMITER || type == CSSRegionContexts.CSS_DECLARATION_DELIMITER) { |
| fTargetNode = node.getParentNode(); |
| } else { |
| fTargetNode = node; |
| } |
| // fTargetNode = (bOverSemiColon) ? node.getParentNode() : node; |
| } else if (CSSUtil.getStructuredDocumentRegionType(flatNode) == CSSRegionContexts.CSS_RBRACE) { |
| fTargetNode = node.getParentNode(); |
| } else { |
| fTargetNode = node; |
| } |
| |
| return; |
| } |
| |
| /** |
| * |
| */ |
| private void initializeTargetPos() { |
| if (fCursorPos == 0 || isSpecialDelimiterRegion(fCursorPos - 1)) { |
| fTargetPos = fCursorPos; |
| } else { |
| fTargetPos = fCursorPos - 1; |
| } |
| |
| //deal with the leading quote |
| if(fQuote != 0) { |
| fTargetPos--; |
| } |
| } |
| |
| /** |
| * |
| */ |
| private void initializeTargetText() { |
| ITextRegion targetRegion = getTargetRegion(); |
| IStructuredDocumentRegion documentRegion = getTargetDocumentRegion(); |
| if (targetRegion == null) { |
| fReplaceBegin = fCursorPos; |
| fTextToReplace = ""; //$NON-NLS-1$ |
| fTextToCompare = ""; //$NON-NLS-1$ |
| } else { |
| String regionText = documentRegion.getText(targetRegion); |
| int regionStart = documentRegion.getStartOffset(targetRegion); |
| if (!fSelected && (regionStart == fCursorPos || regionText.trim().length() == 0 || regionStart + regionText.length() - 1 < this.fTargetPos)) { |
| // to insertion |
| fReplaceBegin = fCursorPos; |
| fTextToReplace = ""; //$NON-NLS-1$ |
| fTextToCompare = ""; //$NON-NLS-1$ |
| try { |
| if (fLength > 0) |
| fTextToReplace = fStructuredDocument.get(fReplaceBegin, fLength); |
| } |
| catch (BadLocationException e) { |
| } |
| } else { |
| // to replace |
| fReplaceBegin = regionStart; |
| fTextToReplace = regionText; |
| |
| //math to deal with leading quote |
| int matchLength = fCursorPos - fReplaceBegin; |
| if(fQuote != 0) { |
| matchLength--; |
| fReplaceBegin++; |
| } |
| |
| if(fCursorPos >= fReplaceBegin && regionText.trim().length() >= (matchLength)) |
| fTextToCompare = regionText.substring(0, matchLength); |
| else |
| fTextToCompare = ""; //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| private boolean isSpecialDelimiterRegion(int pos) { |
| ITextRegion region = getRegionByOffset(pos); |
| String type = null; |
| if(region != null) { |
| type = region.getType(); |
| } |
| return (type != null) &&((type == CSSRegionContexts.CSS_LBRACE || |
| type == CSSRegionContexts.CSS_RBRACE || |
| type == CSSRegionContexts.CSS_DELIMITER || |
| type == CSSRegionContexts.CSS_DECLARATION_SEPARATOR || |
| type == CSSRegionContexts.CSS_DECLARATION_DELIMITER || |
| type == CSSRegionContexts.CSS_DECLARATION_VALUE_OPERATOR || |
| type == CSSRegionContexts.CSS_S)); |
| } |
| /** |
| * |
| */ |
| boolean isTargetPosAfterOf(String regionType) { |
| int start = ((IndexedRegion) fTargetNode).getStartOffset(); |
| if (start < 0 || ((IndexedRegion) fTargetNode).getEndOffset() <= 0) { |
| return false; |
| } |
| |
| RegionIterator iRegion = new RegionIterator(fStructuredDocument, start); |
| while (iRegion.hasNext()) { |
| ITextRegion region = iRegion.next(); |
| if (fTargetPos < iRegion.getStructuredDocumentRegion().getTextEndOffset(region)) { |
| break; |
| } |
| if (region.getType() == regionType) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * |
| */ |
| boolean isTargetPosBeforeOf(String regionType) { |
| return !isTargetPosAfterOf(regionType); |
| } |
| |
| /** |
| * @return boolean |
| * @param regionType |
| * java.lang.String |
| */ |
| boolean targetFollows(String regionType) { |
| RegionIterator iRegion; |
| ITextRegion region = null; |
| if (fStructuredDocument.getLength() <= fTargetPos) { |
| iRegion = new RegionIterator(fStructuredDocument, fStructuredDocument.getLength() - 1); |
| } else { |
| iRegion = new RegionIterator(fStructuredDocument, fTargetPos); |
| try { |
| if (!Character.isWhitespace(fStructuredDocument.getChar(fTargetPos)) && iRegion.hasPrev()) { |
| region = iRegion.prev(); |
| } |
| } catch (BadLocationException e) { |
| iRegion = new RegionIterator(fStructuredDocument, fStructuredDocument.getLength() - 1); |
| } |
| } |
| while (iRegion.hasPrev()) { |
| region = iRegion.prev(); |
| String type = region.getType(); |
| if (type == CSSRegionContexts.CSS_S || type == CSSRegionContexts.CSS_COMMENT) { |
| continue; |
| } else { |
| break; |
| } |
| } |
| if (region != null && region.getType() == regionType) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| /** |
| * |
| */ |
| boolean targetHas(String regionType) { |
| int end = ((IndexedRegion) fTargetNode).getEndOffset(); |
| if (getTargetPos() < 0 || end <= 0) { |
| return false; |
| } |
| RegionIterator iRegion = new RegionIterator(fStructuredDocument, getTargetPos()); |
| while (iRegion.hasNext()) { |
| ITextRegion region = iRegion.next(); |
| if (end <= iRegion.getStructuredDocumentRegion().getStartOffset(region)) { |
| break; |
| } |
| if (region.getType() == regionType) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| RegionIterator getRegionIterator() { |
| return new RegionIterator(getStructuredDocument(), getTargetPos()); |
| } |
| |
| /** |
| * |
| */ |
| CSSMetaModel getMetaModel() { |
| CSSMetaModelFinder finder = CSSMetaModelFinder.getInstance(); |
| ICSSModel model = getModel(); |
| if (model.getOwnerDOMNode() != null) { |
| return finder.findMetaModelFor(model.getOwnerDOMNode()); |
| } |
| return finder.findMetaModelFor(getModel()); |
| } |
| } |