blob: b605610a89dee1713b3a1c1d338aaa67e8969056 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2005 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.core.internal.document;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts;
import org.eclipse.wst.css.core.internal.parserz.CSSTextToken;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSSelectorCombinator;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSSelectorItem;
/**
*
*/
public class CSSSelectorParser {
private final static int IDLE = 0;
private final static int ATTRIBUTE = 1;
private final static int SIMPLE = 2;
private List fTokens = null;
private List fItems = null;
private List fErrors = null;
/**
*
*/
CSSSelectorParser(List tokens) {
super();
fTokens = new ArrayList(tokens);
}
/**
*
*/
List getSelector() {
if (fItems == null) {
parseSelector();
}
return fItems;
}
/**
*
*/
private void parseSelector() {
fItems = new ArrayList();
List attrBuf = null;
CSSSimpleSelector item = null;
int status = IDLE;
Iterator i = fTokens.iterator();
while (i.hasNext()) {
CSSTextToken token = (CSSTextToken) i.next();
if (token == null || token.kind == CSSRegionContexts.CSS_S || token.kind == CSSRegionContexts.CSS_COMMENT) {
continue;
}
switch (status) {
case IDLE :
if (isTag(token)) {
item = createSimple();
appendTag(item, token);
status = SIMPLE;
}
else if (isID(token)) {
item = createSimple();
appendID(item, token);
status = SIMPLE;
}
else if (isClass(token)) {
item = createSimple();
appendClass(item, token);
status = SIMPLE;
}
else if (isPseudo(token)) {
item = createSimple();
appendPseudo(item, token);
status = SIMPLE;
}
else if (isAttributeBegin(token)) {
item = createSimple();
status = ATTRIBUTE;
}
else {
addError(token);
}
break;
case SIMPLE :
if (isID(token)) {
appendID(item, token);
}
else if (isClass(token)) {
appendClass(item, token);
}
else if (isPseudo(token)) {
appendPseudo(item, token);
}
else if (isAttributeBegin(token)) {
status = ATTRIBUTE;
}
else if (isCombinator(token)) { // combinator
closeItem(item);
closeItem(createCombinator(token));
status = IDLE;
}
else {
addError(token);
}
break;
case ATTRIBUTE :
if (isAttributeEnd(token)) {
appendAttribute(item, attrBuf);
attrBuf = null;
status = SIMPLE;
}
else {
if (attrBuf == null) {
attrBuf = new ArrayList();
}
attrBuf.add(token);
if (!isAttributeContent(token)) {
addError(token);
}
}
break;
default :
break;
}
}
closeItem(item);
}
/**
* @param token
* @return
*/
private boolean isAttributeContent(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_ATTRIBUTE_NAME || type == CSSRegionContexts.CSS_SELECTOR_ATTRIBUTE_OPERATOR || type == CSSRegionContexts.CSS_SELECTOR_ATTRIBUTE_VALUE);
}
/**
* @param token
* @return
*/
private boolean isAttributeEnd(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_ATTRIBUTE_END);
}
/**
* @param token
* @return
*/
private boolean isAttributeBegin(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_ATTRIBUTE_START);
}
/**
* @param token
* @return
*/
private boolean isCombinator(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_COMBINATOR);
}
/**
* @param token
* @return
*/
private boolean isPseudo(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_PSEUDO);
}
/**
* @param token
* @return
*/
private boolean isClass(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_CLASS);
}
/**
* @param token
* @return
*/
private boolean isID(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_ID);
}
/**
* @param token
* @return
*/
private boolean isTag(CSSTextToken token) {
String type = token.kind;
return (type == CSSRegionContexts.CSS_SELECTOR_ELEMENT_NAME || type == CSSRegionContexts.CSS_SELECTOR_UNIVERSAL || type == CSSRegionContexts.CSS_UNKNOWN);
}
private CSSSimpleSelector createSimple() {
return new CSSSimpleSelector();
}
/**
* if " " or "+" or ">" appeared, close simpleselector and add new
* combinator
*/
private CSSSelectorCombinator createCombinator(CSSTextToken token) {
char type = 0;
String str = token.image;
if (str.trim().length() == 0) { // space
type = ICSSSelectorCombinator.DESCENDANT;
}
else if (str.equals("+")) { //$NON-NLS-1$
type = ICSSSelectorCombinator.ADJACENT;
}
else if (str.equals(">")) { //$NON-NLS-1$
type = ICSSSelectorCombinator.CHILD;
}
if (0 < type) {
return new CSSSelectorCombinator(type);
}
else {
return null;
}
}
/**
*
*/
private void closeItem(ICSSSelectorItem item) {
if (item != null) {
fItems.add(item);
}
}
private void appendTag(CSSSimpleSelector item, CSSTextToken token) {
item.setName(token.image);
if (token.kind == CSSRegionContexts.CSS_UNKNOWN) {
addError(token);
}
}
/**
* if "#xxxx" appeared, add ID to current selector
*/
private void appendID(CSSSimpleSelector item, CSSTextToken token) {
String text = token.toString();
String idContent = text.substring(1, text.length());
item.addID(idContent);
}
/**
* if ".xxxx" appeared, add Class to current selector
*/
private void appendClass(CSSSimpleSelector item, CSSTextToken token) {
String text = token.toString();
String classContent = text.substring(1, text.length());
item.addClass(classContent);
if (Character.isDigit(classContent.charAt(0))) {
addError(token);
}
}
/**
* if ":xxxx" appeared, add Pseudo(element/class) to current selector
*/
private void appendPseudo(CSSSimpleSelector item, CSSTextToken token) {
String text = token.toString();
String pseudoContent = text.substring(1, text.length());
item.addPseudoName(pseudoContent);
}
/**
*
*/
private void appendAttribute(CSSSimpleSelector item, List tokens) {
StringBuffer buf = new StringBuffer();
CSSTextToken token;
Iterator i = tokens.iterator();
while (i.hasNext()) {
token = (CSSTextToken) i.next();
buf.append(token.image);
}
item.addAttribute(buf.toString());
}
/**
*
*/
List getSelectorTags() {
List tagList = new ArrayList();
return tagList;
}
/**
*
*/
private void addError(CSSTextToken token) {
if (fErrors == null) {
fErrors = new ArrayList();
}
fErrors.add(token);
}
/**
*
*/
Iterator getErrors() {
return (fErrors == null) ? Collections.EMPTY_LIST.iterator() : fErrors.iterator();
}
}