| /******************************************************************************* |
| * Copyright (c) 2004, 2006 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; |
| } |
| |
| } |