blob: 342006386356474ae46c7f77650c3d25a6add5a6 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2004 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 java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.wst.css.core.internal.metamodel.CSSMMDescriptor;
import org.eclipse.wst.css.core.internal.metamodel.CSSMMFunction;
import org.eclipse.wst.css.core.internal.metamodel.CSSMMNode;
import org.eclipse.wst.css.core.internal.metamodel.CSSMMNumber;
import org.eclipse.wst.css.core.internal.metamodel.CSSMMProperty;
import org.eclipse.wst.css.core.internal.metamodel.CSSMMUnit;
import org.eclipse.wst.css.core.internal.metamodel.util.CSSFunctionID;
import org.eclipse.wst.css.core.internal.metamodel.util.CSSMetaModelUtil;
import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSNode;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSPrimitiveValue;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSStyleDeclItem;
import org.eclipse.wst.css.core.internal.util.CSSUtil;
import org.eclipse.wst.css.core.internal.util.RegionIterator;
import org.eclipse.wst.css.core.preferences.CSSPreferenceHelper;
import org.eclipse.wst.css.ui.internal.image.CSSImageType;
import org.eclipse.wst.sse.core.IndexedRegion;
import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.text.ITextRegion;
import org.w3c.dom.css.CSSFontFaceRule;
class CSSProposalGeneratorForDeclarationValue extends CSSProposalGenerator {
private static final String IMPORTANT = "!important"; //$NON-NLS-1$
private boolean fUseUpperCase;
private boolean fAppendSemiColon;
/**
* CSSProposalGeneratorForDeclarationValue constructor comment.
*
* @param context
* com.ibm.sed.contentassist.old.css.CSSContentAssistContext
*/
CSSProposalGeneratorForDeclarationValue(CSSContentAssistContext context) {
super(context);
fUseUpperCase = CSSPreferenceHelper.getInstance().isPropValueUpperCase();
}
/**
*
*/
private void addFunction(List candidates, CSSMMFunction prop) {
String text = prop.toString();
if (!isMatch(text)) {
return;
}
int cursorPos = 0;
StringBuffer buf = new StringBuffer();
if (prop.getName().equals(CSSFunctionID.F_URI)) {
StringAndOffset sao = generateURI();
buf.append(sao.fString);
cursorPos = sao.fOffset;
} else {
buf.append(prop.toString());
cursorPos = buf.length();
StringAndOffset sao = generateParenthesis();
buf.append(sao.fString);
cursorPos += sao.fOffset;
}
text = buf.toString();
text = (fUseUpperCase) ? text.toUpperCase() : text.toLowerCase();
CSSCACandidate item = new CSSCACandidate();
item.setReplacementString(text);
item.setCursorPosition(cursorPos);
item.setDisplayString(text);
item.setImageType(CSSImageType.VALUE_FUNCTION);
appendSemiColon(item);
candidates.add(item);
}
/**
*
*/
private void addNumber(List candidates, CSSMMNumber prop) {
String fullText = fContext.getTextToReplace();
// skip number
int unitIndex = -1;
for (int i = 0; i < fullText.length(); i++) {
if (Character.isDigit(fullText.charAt(i))) {
unitIndex = i + 1;
} else {
break;
}
}
String unitSubText = ""; //$NON-NLS-1$
String numSubText = ""; //$NON-NLS-1$
if (0 <= unitIndex) {
numSubText = fullText.substring(0, unitIndex);
if (unitIndex < fullText.length()) {
unitSubText = fullText.substring(unitIndex);
}
} else {
unitSubText = fullText;
}
Iterator i = prop.getDescendants();
while (i.hasNext()) {
CSSMMUnit unit = (CSSMMUnit) i.next();
String unitString = unit.getUnitString();
if ((0 < unitSubText.length() && unitString.indexOf(unitSubText) != 0) || (0 < numSubText.length() && unitString.equals("#"))) { //$NON-NLS-1$
continue;
}
String text = numSubText + unitString;
text = (fUseUpperCase) ? text.toUpperCase() : text.toLowerCase();
CSSCACandidate item = new CSSCACandidate();
item.setReplacementString(text);
if (0 < numSubText.length() || text.equals("#")) { //$NON-NLS-1$
item.setCursorPosition(text.length());
} else {
item.setCursorPosition(0);
}
item.setDisplayString(text);
item.setImageType(CSSImageType.VALUE_NUMBER);
appendSemiColon(item);
candidates.add(item);
}
}
/**
*
*/
private void checkSemiColon() {
fAppendSemiColon = false;
ITextRegion targetRegion = fContext.getTargetRegion();
if (targetRegion != null && targetRegion.getType() != CSSRegionContexts.CSS_DECLARATION_DELIMITER) {
// find trailing ":" or ";"
// if ":" before ";" is found, add ";"
RegionIterator iterator = fContext.getRegionIterator();
IStructuredDocumentRegion container = iterator.getStructuredDocumentRegion();
while (iterator.hasNext()) {
ITextRegion region = iterator.next();
if (iterator.getStructuredDocumentRegion() != container) {
break;
}
if (region.getType() == CSSRegionContexts.CSS_DECLARATION_SEPARATOR) {
fAppendSemiColon = true;
break;
}
}
if (!fAppendSemiColon) {
// second chance:
// leading IStructuredDocumentRegion is not ";"
IStructuredDocumentRegion nextStructuredDocumentRegion = CSSUtil.findNextSignificantNode(container);
if (CSSUtil.getStructuredDocumentRegionType(nextStructuredDocumentRegion) != CSSRegionContexts.CSS_DECLARATION_DELIMITER) {
fAppendSemiColon = true;
}
}
}
}
/**
*
*/
private void appendSemiColon(CSSCACandidate item) {
if (fAppendSemiColon) {
String replacementString = item.getReplacementString();
item.setReplacementString(replacementString + ";"); //$NON-NLS-1$
int cursorPosition = item.getCursorPosition();
if (replacementString.length() <= cursorPosition) {
// cursorpos is tail of string
cursorPosition++;
item.setCursorPosition(cursorPosition);
}
}
}
/**
*
*/
private void addSemiColon(List candidates) {
ICSSNode targetNode = fContext.getTargetNode();
if (targetNode instanceof ICSSStyleDeclItem) {
ICSSNode firstChild = targetNode.getFirstChild();
if (firstChild == null) {
return;
}
if (firstChild instanceof IndexedRegion) {
int startOffset = ((IndexedRegion) firstChild).getStartOffset();
if (fContext.getCursorPos() <= startOffset) {
return;
}
}
}
boolean bAddCloser = false;
ITextRegion targetRegion = fContext.getTargetRegion();
if (targetRegion != null && targetRegion.getType() != CSSRegionContexts.CSS_DECLARATION_DELIMITER) {
// find trailing ":" or ";"
// if ":" before ";" is found, add ";"
RegionIterator iterator = fContext.getRegionIterator();
IStructuredDocumentRegion container = iterator.getStructuredDocumentRegion();
while (iterator.hasNext()) {
ITextRegion region = iterator.next();
if (iterator.getStructuredDocumentRegion() != container) {
break;
}
if (region.getType() == CSSRegionContexts.CSS_DECLARATION_SEPARATOR) {
bAddCloser = true;
break;
}
}
if (!bAddCloser) {
// second chance:
// leading IStructuredDocumentRegion is not ";"
IStructuredDocumentRegion nextStructuredDocumentRegion = CSSUtil.findNextSignificantNode(container);
if (CSSUtil.getStructuredDocumentRegionType(nextStructuredDocumentRegion) != CSSRegionContexts.CSS_DECLARATION_DELIMITER) {
bAddCloser = true;
}
}
}
if (bAddCloser) {
CSSCACandidate item = new CSSCACandidate();
String text = fContext.getTextToReplace() + ";";//$NON-NLS-1$
item.setReplacementString(text);
item.setCursorPosition(text.length());
item.setDisplayString(";");//$NON-NLS-1$
item.setImageType(null);
candidates.add(item);
}
}
/**
*
*/
private void addString(List candidates, String text) {
if (!isMatch(text)) {
return;
}
text = (fUseUpperCase) ? text.toUpperCase() : text.toLowerCase();
CSSCACandidate item = new CSSCACandidate();
item.setReplacementString(text);
item.setCursorPosition(text.length());
item.setDisplayString(text);
item.setImageType(CSSImageType.VALUE_STRING);
appendSemiColon(item);
candidates.add(item);
}
private void addImportant(List candidates) {
ICSSNode targetNode = fContext.getTargetNode();
while (targetNode instanceof ICSSPrimitiveValue) {
targetNode = targetNode.getParentNode();
}
if (!(targetNode instanceof ICSSStyleDeclItem)) {
return;
}
// 1. has no priority region
// 2. cursor position is after of last child
// 3. normal isMatch method
String priority = ((ICSSStyleDeclItem) targetNode).getPriority();
if (priority == null || priority.length() == 0) {
ICSSNode lastChild = targetNode.getLastChild();
if (lastChild instanceof IndexedRegion) {
int startOffset = ((IndexedRegion) lastChild).getStartOffset();
// int endOffset = ((IndexedRegion)lastChild).getEndOffset();
if (startOffset < fContext.getCursorPos() && isMatch(IMPORTANT)) {
CSSCACandidate item = new CSSCACandidate();
item.setReplacementString(IMPORTANT);
item.setCursorPosition(IMPORTANT.length());
item.setDisplayString(IMPORTANT);
item.setImageType(CSSImageType.VALUE_STRING);
appendSemiColon(item);
candidates.add(item);
}
}
}
}
/**
* getCandidates method comment.
*/
protected Iterator getCandidates() {
List candidates = new ArrayList();
checkSemiColon(); // check should add semi-colon or not
String name = getPropertyName();
if (name != null) {
CSSMetaModelUtil util = new CSSMetaModelUtil(fContext.getMetaModel());
Iterator i = Collections.EMPTY_LIST.iterator();
if (isFontFaceRule()) {
CSSMMDescriptor desc = util.getDescriptor(name);
if (desc != null) {
i = desc.getValues();
}
} else {
CSSMMProperty prop = util.getProperty(name);
if (prop != null) {
i = prop.getValues();
}
}
while (i.hasNext()) {
CSSMMNode val = (CSSMMNode) i.next();
String valueType = val.getType();
if (valueType == CSSMMNode.TYPE_KEYWORD) {
addString(candidates, val.toString());
} else if (valueType == CSSMMNode.TYPE_NUMBER) {
addNumber(candidates, (CSSMMNumber) val);
} else if (valueType == CSSMMNode.TYPE_FUNCTION) {
addFunction(candidates, (CSSMMFunction) val);
}
}
}
addImportant(candidates);
addSemiColon(candidates);
return candidates.iterator();
}
/**
* @return java.lang.String
*/
private String getPropertyName() {
ICSSNode targetNode = fContext.getTargetNode();
while (targetNode instanceof ICSSPrimitiveValue) {
targetNode = targetNode.getParentNode();
}
if (targetNode instanceof ICSSStyleDeclItem) {
return ((ICSSStyleDeclItem) targetNode).getPropertyName();
} else {
return null;
}
}
/**
*
*/
private boolean isFontFaceRule() {
ICSSNode targetNode = fContext.getTargetNode();
while (targetNode instanceof ICSSPrimitiveValue) {
targetNode = targetNode.getParentNode();
}
if (targetNode instanceof ICSSStyleDeclItem) {
targetNode = targetNode.getParentNode(); // get Declaration
if (targetNode != null) {
// inline style has no rule node
targetNode = targetNode.getParentNode(); // get rule
}
}
return (targetNode instanceof CSSFontFaceRule);
}
}