blob: 250d95242d89ad4c91cf35ece13f09cc16d08634 [file] [log] [blame]
/*******************************************************************************
* 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());
}
}