blob: fd3ac8069b45df4fdccc400592c4b1a0ba2568f7 [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.core.internal.parser;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.wst.css.core.internal.Logger;
import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts;
import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.core.internal.util.Debug;
public class CSSSourceParser implements RegionParser {
public static final int MODE_STYLESHEET = 0;
public static final int MODE_DECLARATION = 1;
public static final int MODE_DECLARATION_VALUE = 2;
private long fStartTime;
private long fStopTime;
private ICSSTokenizer fTokenizer;
public void setParserMode(int mode) {
int initialState;
int bufsize;
switch (mode) {
case MODE_STYLESHEET :
initialState = CSSTokenizer.YYINITIAL;
bufsize = CSSTokenizer.BUFFER_SIZE_NORMAL;
break;
case MODE_DECLARATION :
initialState = CSSTokenizer.ST_DECLARATION;
bufsize = CSSTokenizer.BUFFER_SIZE_NORMAL;
break;
case MODE_DECLARATION_VALUE :
initialState = CSSTokenizer.ST_DECLARATION_PRE_VALUE;
bufsize = CSSTokenizer.BUFFER_SIZE_SMALL;
break;
default :
return;
}
if (0 < initialState) {
ICSSTokenizer tokenizer = getTokenizer();
tokenizer.setInitialState(initialState);
tokenizer.setInitialBufferSize(bufsize);
}
}
public IStructuredDocumentRegion getDocumentRegions() {
IStructuredDocumentRegion headnode = null;
if (headnode == null) {
if (Debug.perfTest) {
fStartTime = System.currentTimeMillis();
}
headnode = parseNodes();
if (Debug.perfTest) {
fStopTime = System.currentTimeMillis();
System.out.println(" -- creating nodes of IStructuredDocument -- "); //$NON-NLS-1$
System.out.println(" Time parse and init all regions: " + (fStopTime - fStartTime) + " (msecs)"); //$NON-NLS-2$//$NON-NLS-1$
// System.out.println(" for " + fRegions.size() + "
// Regions");//$NON-NLS-2$//$NON-NLS-1$
System.out.println(" and " + _countNodes(headnode) + " Nodes"); //$NON-NLS-2$//$NON-NLS-1$
}
}
return headnode;
}
public List getRegions() {
IStructuredDocumentRegion headNode = null;
if (!getTokenizer().isEOF()) {
headNode = getDocumentRegions();
// throw new IllegalStateException("parsing has not finished");
}
// for memory recovery, we assume if someone
// requests all regions, we can reset our big
// memory consuming objects
// but the new "getRegions" method is then more expensive.
// I don't think its used much, though.
List localRegionsList = getRegions(headNode);
primReset();
return localRegionsList;
}
/**
* Method getRegions.
*
* @param headNode
* @return List
*/
protected List getRegions(IStructuredDocumentRegion headNode) {
List allRegions = new ArrayList();
IStructuredDocumentRegion currentNode = headNode;
while (currentNode != null) {
ITextRegionList nodeRegions = currentNode.getRegions();
for (int i = 0; i < nodeRegions.size(); i++) {
allRegions.add(nodeRegions.get(i));
}
currentNode = currentNode.getNext();
}
return allRegions;
}
public void reset(Reader reader) {
primReset();
getTokenizer().reset(reader, 0);
}
public void reset(Reader reader, int offset) {
reset(reader);
}
public void reset(String input) {
reset(new StringReader(input));
}
public void reset(String input, int offset) {
reset(input);
}
public RegionParser newInstance() {
return new CSSSourceParser();
}
private IStructuredDocumentRegion parseNodes() {
// regions are initially reported as complete offsets within the
// scanned input
// they are adjusted here to be indexes from the currentNode's start
// offset
IStructuredDocumentRegion headNode = null;
IStructuredDocumentRegion lastNode = null;
ITextRegion region = null;
IStructuredDocumentRegion currentNode = null;
String type = null;
String currentRegionType = null;
while ((region = getNextRegion()) != null) {
type = region.getType();
if (mustBeStart(type, currentRegionType) && currentNode != null) {
currentNode.setEnded(true);
}
if ((currentNode != null && currentNode.isEnded()) || currentNode == null) {
if (currentNode != null && !currentNode.isEnded()) {
currentNode.setEnded(true);
}
lastNode = currentNode;
currentNode = createStructuredDocumentRegion(type);
currentRegionType = type;
if (lastNode != null) {
lastNode.setNext(currentNode);
}
currentNode.setPrevious(lastNode);
currentNode.setStart(region.getStart());
}
currentNode.addRegion(region);
currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart());
region.adjustStart(-currentNode.getStart());
if (mustBeEnd(type)) {
currentNode.setEnded(true);
}
if (headNode == null && currentNode != null) {
headNode = currentNode;
}
}
if (currentNode != null && !currentNode.isEnded()) {
currentNode.setEnded(true);
}
primReset();
return headNode;
}
private IStructuredDocumentRegion createStructuredDocumentRegion(String type) {
return CSSStructuredDocumentRegionFactory.createRegion(type);
}
/**
* currently public but may be made default access protected in future.
*/
protected boolean mustBeStart(String type, String docRegionType) {
return ((type == CSSRegionContexts.CSS_DELIMITER || type == CSSRegionContexts.CSS_LBRACE || type == CSSRegionContexts.CSS_RBRACE || type == CSSRegionContexts.CSS_IMPORT || type == CSSRegionContexts.CSS_PAGE || type == CSSRegionContexts.CSS_MEDIA || type == CSSRegionContexts.CSS_FONT_FACE || type == CSSRegionContexts.CSS_CHARSET || type == CSSRegionContexts.CSS_ATKEYWORD || type == CSSRegionContexts.CSS_DECLARATION_PROPERTY || type == CSSRegionContexts.CSS_DECLARATION_DELIMITER) || (docRegionType == CSSRegionContexts.CSS_DECLARATION_PROPERTY && type == CSSRegionContexts.CSS_S) || (!CSSRegionUtil.isSelectorBegginingType(docRegionType) && (type == CSSRegionContexts.CSS_SELECTOR_ELEMENT_NAME || type == CSSRegionContexts.CSS_SELECTOR_UNIVERSAL || type == CSSRegionContexts.CSS_SELECTOR_PSEUDO || type == CSSRegionContexts.CSS_SELECTOR_CLASS || type == CSSRegionContexts.CSS_SELECTOR_ID || type == CSSRegionContexts.CSS_SELECTOR_ATTRIBUTE_START)));
}
/**
* currently public but may be made default access protected in future.
*/
protected boolean mustBeEnd(String type) {
return (type == CSSRegionContexts.CSS_DELIMITER || type == CSSRegionContexts.CSS_LBRACE || type == CSSRegionContexts.CSS_RBRACE || type == CSSRegionContexts.CSS_DECLARATION_DELIMITER);
}
private ITextRegion getNextRegion() {
ITextRegion region = null;
try {
region = getTokenizer().getNextToken();
// DMW: 2/12/03 Removed state
// if (region != null) {
// fRegions.add(region);
// }
return region;
}
catch (StackOverflowError e) {
Logger.logException(getClass().getName() + ": input could not be parsed correctly at position " + getTokenizer().getOffset(), e); //$NON-NLS-1$
throw e;
}
catch (Exception e) {
Logger.logException(getClass().getName() + ": input could not be parsed correctly at position " + getTokenizer().getOffset() + " (" + e.getLocalizedMessage() + ")", e); //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$
}
return null;
}
private void primReset() {
getTokenizer().reset(new char[0]);
}
/**
* currently public but may be made default access protected in future.
*/
public ICSSTokenizer getTokenizer() {
if (fTokenizer == null) {
fTokenizer = new CSSTokenizer();
}
return fTokenizer;
}
/**
* This is a simple utility to count nodes. Used only for debug
* statements.
*/
private int _countNodes(IStructuredDocumentRegion nodes) {
int result = 0;
IStructuredDocumentRegion countNode = nodes;
while (countNode != null) {
result++;
countNode = countNode.getNext();
}
return result;
}
}