/******************************************************************************* * Copyright (c) 2004, 2010 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.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts; import org.eclipse.wst.css.core.internal.provisional.document.ICSSCharsetRule; import org.eclipse.wst.css.core.internal.provisional.document.ICSSDocument; import org.eclipse.wst.css.core.internal.provisional.document.ICSSImportRule; import org.eclipse.wst.css.core.internal.provisional.document.ICSSMediaRule; 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.provisional.document.ICSSPageRule; import org.eclipse.wst.css.core.internal.provisional.document.ICSSRuleContainer; import org.eclipse.wst.css.core.internal.provisional.document.ICSSStyleDeclItem; import org.eclipse.wst.css.core.internal.provisional.document.ICSSStyleRule; import org.eclipse.wst.css.core.internal.text.StructuredDocumentWalker; import org.eclipse.wst.css.core.internal.util.CSSUtil; import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; 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.IStructuredDocumentRegionList; 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.text.BasicStructuredDocumentRegion; import org.eclipse.wst.sse.core.internal.text.CoreNodeList; import org.eclipse.wst.sse.core.internal.text.TextRegionListImpl; import org.eclipse.wst.sse.core.internal.util.Assert; import org.w3c.dom.css.CSSFontFaceRule; import org.w3c.dom.css.CSSRule; import org.w3c.dom.css.CSSStyleDeclaration; /** * currently public but may be made default access protected in future. */ public class CSSModelParser { private ICSSDocument fDocument = null; private CSSModelCreationContext fCreationContext = null; private CSSModelDeletionContext fDeletionContext = null; private CSSModelUpdateContext fUpdateContext = null; private CSSModelNodeFeeder fFeeder = null; private StructuredDocumentWalker fStructuredDocumentWalker = null; private boolean fParseFloating = false; /** * CSSModelParser constructor comment. */ private CSSModelParser() { super(); } /** * currently provded this method but may be removed in future. */ protected boolean isParseFloating(){ return fParseFloating; } /** * currently provded this method but may be removed in future. */ protected CSSModelCreationContext getCreationContext(){ return fCreationContext; } /** * currently provded this method but may be removed in future. */ protected boolean isUpdateContextActive(){ return fUpdateContext != null ? fUpdateContext.isActive() : false; } /** * currently public but may be made default access protected in future. */ public CSSModelParser(ICSSDocument doc) { this(); fDocument = doc; fCreationContext = new CSSModelCreationContext((CSSNodeImpl)doc); fDeletionContext = new CSSModelDeletionContext(doc); fUpdateContext = new CSSModelUpdateContext(); fFeeder = new CSSModelNodeFeeder(doc, fUpdateContext); } /** * */ void changeStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { replaceStructuredDocumentRegions(new CoreNodeList(flatNode, flatNode), new CoreNodeList(flatNode, flatNode)); } /** * */ void changeRegion(IStructuredDocumentRegion flatNode, ITextRegion region) { if (flatNode == null || region == null) { return; } if (fDocument == null) { return; } changeStructuredDocumentRegion(flatNode); } /** * */ private void checkNextNode(IStructuredDocumentRegion flatNode, String type) { IStructuredDocumentRegion next = CSSUtil.findNextSignificantNode(flatNode); if (CSSUtil.getStructuredDocumentRegionType(next) == type) { fCreationContext.setReparseStart(flatNode.getEnd()); fCreationContext.setReparseEnd(next.getEnd()); } } /** * */ private void cleanupDeletionContext() { // setupDeletionContext(null); } /** * */ boolean cleanupFirstNode(IStructuredDocumentRegion firstNode, CSSStructuredDocumentRegionContainer target) { if (firstNode == null || target == null) { return false; } if (firstNode == target.getFirstStructuredDocumentRegion()) { IStructuredDocumentRegion nextNode = fStructuredDocumentWalker.getNextNodeInCurrent(firstNode); IStructuredDocumentRegion lastNode = target.getLastStructuredDocumentRegion(); if (lastNode == null || fStructuredDocumentWalker.isOldNode(lastNode) || nextNode == null || lastNode.getStartOffset() < nextNode.getStartOffset()) { target.setRangeStructuredDocumentRegion(null, null); } else { target.setFirstStructuredDocumentRegion(nextNode); } ICSSNode parent = target.getParentNode(); if (parent instanceof CSSStructuredDocumentRegionContainer) { cleanupFirstNode(firstNode, (CSSStructuredDocumentRegionContainer) parent); return true; } } return false; } /** * */ boolean cleanupLastNode(IStructuredDocumentRegion lastNode, CSSStructuredDocumentRegionContainer target) { if (lastNode == null || target == null) { return false; } if (lastNode == target.getLastStructuredDocumentRegion()) { IStructuredDocumentRegion prevNode = fStructuredDocumentWalker.getPrevNodeInCurrent(lastNode); IStructuredDocumentRegion firstNode = target.getFirstStructuredDocumentRegion(); if (firstNode == null || fStructuredDocumentWalker.isOldNode(firstNode) || prevNode == null || prevNode.getStartOffset() < firstNode.getStartOffset()) { target.setRangeStructuredDocumentRegion(null, null); } else { target.setLastStructuredDocumentRegion(prevNode); } ICSSNode parent = target.getParentNode(); if (parent instanceof CSSStructuredDocumentRegionContainer) { cleanupLastNode(lastNode, (CSSStructuredDocumentRegionContainer) parent); return true; } } return false; } /** * */ int cleanupUpdateContext() { int remains = fUpdateContext.getNodeCount(); fUpdateContext.cleanupContext(); return remains; } /** * create floating CSS rule (owner document will be set) this is for * CSSStyleSheet.createCSSRule(String) * * @return org.w3c.dom.css.CSSRule */ CSSRule createCSSRule(IStructuredDocumentRegionList flatNodes) { if (flatNodes == null) { return null; } fParseFloating = true; // setup creation context fCreationContext.clear(); fCreationContext.setTargetNode(null); fCreationContext.setNextNode(null); CSSRuleImpl parentRule = null; for (Enumeration e = flatNodes.elements(); e.hasMoreElements();) { IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); if (flatNode == null) { continue; } CSSNodeImpl modified = insertStructuredDocumentRegion(flatNode); if (parentRule == null && modified instanceof CSSRuleImpl) { parentRule = (CSSRuleImpl) modified; } } fParseFloating = false; if (parentRule != null) { CSSModelUtil.cleanupContainer(parentRule); } return parentRule; } /** * */ private IStructuredDocumentRegion findBraceClose(int depth, IStructuredDocumentRegion start, boolean bBreakSibling) { IStructuredDocumentRegion result = null; int braceLevel = 0; IStructuredDocumentRegion prevNode = null; IStructuredDocumentRegion region = start.getNext(); for (; region != null; region = region.getNext()) { int offset = region.getStart(); if (offset < 0) { Assert.isTrue(false); break; } CSSNodeImpl node = getNodeAt(offset); int depthHit = (node != null) ? CSSModelUtil.getDepth(node) : -1; if (depth <= depthHit) { // sibling is found before "}" if (bBreakSibling) { CSSNodeImpl parent = (CSSNodeImpl) node.getParentNode(); while (depth <= CSSModelUtil.getDepth(parent)) { node = parent; parent = (CSSNodeImpl) parent.getParentNode(); } if (parent != null && node != null) { parent.removeChild(node); } } else { result = prevNode; break; } } String type = CSSUtil.getStructuredDocumentRegionType(region); if (type == CSSRegionContexts.CSS_LBRACE) { braceLevel++; } if (type == CSSRegionContexts.CSS_RBRACE) { braceLevel--; if (braceLevel < 0) { result = region; break; } } prevNode = region; } if (result == null && region == null) { // reach the end of document result = prevNode; } return result; } /** * */ private IStructuredDocumentRegionList getStructuredDocumentRegionList(int start, int end) { IStructuredDocumentRegionList nodeList = null; if (0 <= start && start <= end) { ICSSModel model = fDocument.getModel(); if (model instanceof CSSModelImpl) { IStructuredDocument structuredDocument = ((CSSModelImpl) model).getStructuredDocument(); if (structuredDocument != null) { IStructuredDocumentRegion startNode = structuredDocument.getRegionAtCharacterOffset(start); IStructuredDocumentRegion endNode = structuredDocument.getRegionAtCharacterOffset(end - 1); if (startNode != null && endNode != null) { nodeList = new CoreNodeList(startNode, endNode); } } } } return nodeList; } /** * */ private CSSNodeImpl getNodeAt(int offset) { CSSNodeImpl rootNode = fCreationContext.getRootNode(); ICSSNode target = rootNode.getNodeAt(offset); if (target instanceof CSSNodeImpl) { return (CSSNodeImpl) target; } else { return null; } } /** * */ private CSSNodeImpl insertBraceClose(IStructuredDocumentRegion region) { ICSSNode target = CSSModelUtil.findBraceContainer(fCreationContext.getTargetNode()); if (!(target instanceof CSSStructuredDocumentRegionContainer)) { return null; } CSSStructuredDocumentRegionContainer parent = (CSSStructuredDocumentRegionContainer) target; if (CSSModelUtil.isInterruption(parent, region)) { ICSSNode node = parent.getParentNode(); if (node instanceof CSSNodeImpl) { fCreationContext.setReparseStart(parent.getStartOffset()); fCreationContext.setReparseEnd(parent.getEndOffset()); ((CSSNodeImpl) node).removeChild(parent); } return null; } if (parent instanceof ICSSPageRule || parent instanceof ICSSMediaRule || parent instanceof CSSFontFaceRule || parent instanceof ICSSStyleRule) { CSSModelUtil.expandStructuredDocumentRegionContainer(parent, region); if (!CSSModelUtil.isBraceClosed(target)) { fCreationContext.setTargetNode(parent.getParentNode()); fCreationContext.setNextNode(parent.getNextSibling()); return parent; } } return null; } /** * */ private CSSNodeImpl insertBraceOpen(IStructuredDocumentRegion region) { IStructuredDocumentRegion keyRegion = CSSUtil.findPreviousSignificantNode(region); if (keyRegion == null) { return null; } if (!fParseFloating) { CSSNodeImpl node = getNodeAt(keyRegion.getStartOffset()); if (node != null && !(node instanceof ICSSRuleContainer)) { return null; } } String type = CSSUtil.getStructuredDocumentRegionType(keyRegion); CSSNodeImpl inserted = null; if (type == CSSRegionContexts.CSS_PAGE) { inserted = insertPageRule(keyRegion, region); } else if (type == CSSRegionContexts.CSS_MEDIA) { inserted = insertMediaRule(keyRegion, region); } else if (type == CSSRegionContexts.CSS_FONT_FACE) { inserted = insertFontFaceRule(keyRegion, region); } else if (CSSUtil.isSelectorText(keyRegion)) { inserted = insertStyleRule(keyRegion, region); } if (inserted instanceof CSSStructuredDocumentRegionContainer) { // CSSModelUtil.expandStructuredDocumentRegionContainer((CSSStructuredDocumentRegionContainer)inserted, // flatNode); IStructuredDocumentRegion braceClose = findBraceClose(CSSModelUtil.getDepth(inserted), region, (type == CSSRegionContexts.CSS_MEDIA)); if (braceClose != null) { fCreationContext.setReparseStart(region.getEnd()); fCreationContext.setReparseEnd(braceClose.getEnd()); } } return inserted; } /** * */ private CSSNodeImpl insertCharsetRule(IStructuredDocumentRegion beginDocRegion, IStructuredDocumentRegion endDocRegion) { CSSNodeImpl parent = fCreationContext.getTargetNode(); if (!fParseFloating && !(parent instanceof ICSSRuleContainer)) { return null; } ITextRegionList regions = new TextRegionListImpl(beginDocRegion.getRegions()); regions.remove(0); // must be "@charset" ITextRegion encodingRegion = null; while (!regions.isEmpty()) { ITextRegion textRegion = regions.remove(0); if (textRegion == null) { continue; } String type = textRegion.getType(); if (type == CSSRegionContexts.CSS_S || type == CSSRegionContexts.CSS_COMMENT) { continue; } if (type == CSSRegionContexts.CSS_STRING) { encodingRegion = textRegion; break; } else { break; } } if (encodingRegion == null) { return null; } CSSCharsetRuleImpl rule = fFeeder.getCSSCharsetRule(); if (rule == null) { return null; } if (!fUpdateContext.isActive()) { rule.setAttribute(ICSSCharsetRule.ENCODING, beginDocRegion.getText(encodingRegion)); } // setup flat container rule.setRangeStructuredDocumentRegion(beginDocRegion, endDocRegion); CSSAttrImpl attr = rule.getAttributeNode(ICSSCharsetRule.ENCODING); if (attr != null) { attr.setRangeRegion(beginDocRegion, encodingRegion, encodingRegion); } // insert to tree if (!fUpdateContext.isActive() && parent != null) { propagateRangePreInsert(parent, rule); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(rule, next); } else { parent.appendChild(rule); } } return rule; } /** * currently public but may be made default access protected in future. */ protected CSSNodeImpl insertStructuredDocumentRegion(IStructuredDocumentRegion region) { if (fCreationContext == null || region == null) { return null; } String type = ((BasicStructuredDocumentRegion) region).getType(); CSSNodeImpl modified = null; ICSSNode target = fCreationContext.getTargetNode(); if ((fParseFloating && target == null) || target instanceof ICSSRuleContainer) { if (type == CSSRegionContexts.CSS_DELIMITER) { modified = insertSemiColonForRule(region); } else if (type == CSSRegionContexts.CSS_LBRACE) { modified = insertBraceOpen(region); } else if (type == CSSRegionContexts.CSS_RBRACE) { modified = insertBraceClose(region); } else if (type == CSSRegionContexts.CSS_MEDIA || type == CSSRegionContexts.CSS_PAGE || type == CSSRegionContexts.CSS_FONT_FACE || CSSUtil.isSelectorText(region)) { checkNextNode(region, CSSRegionContexts.CSS_LBRACE); } else if (type == CSSRegionContexts.CSS_IMPORT || type == CSSRegionContexts.CSS_CHARSET) { checkNextNode(region, CSSRegionContexts.CSS_DELIMITER); } } else if ((target instanceof CSSRuleDeclContainer || target instanceof CSSStyleDeclaration) && type == CSSRegionContexts.CSS_DECLARATION_PROPERTY) { modified = insertStyleDeclarationItem(region); } else if (target instanceof ICSSStyleDeclItem && type == CSSRegionContexts.CSS_DECLARATION_DELIMITER) { modified = insertSemiColonForStyleDeclarationItem(region); } else if (type == CSSRegionContexts.CSS_RBRACE) { modified = insertBraceClose(region); } // post process if (modified != null) { if (modified instanceof CSSStructuredDocumentRegionContainer) { ((CSSStructuredDocumentRegionContainer) modified).propagateRangeStructuredDocumentRegion(); } } return modified; } /** * */ private void insertStructuredDocumentRegions(IStructuredDocumentRegionList regionList) { for (Enumeration e = regionList.elements(); e.hasMoreElements();) { IStructuredDocumentRegion region = (IStructuredDocumentRegion) e.nextElement(); if (region == null) { continue; } insertStructuredDocumentRegion(region); if (fCreationContext.isToReparse()) { int origStart = region.getEnd(); int origEnd = regionList.item(regionList.getLength() - 1).getEnd(); int newStart = fCreationContext.getReparseStart(); int newEnd = fCreationContext.getReparseEnd(); if (newStart < origStart || origEnd < newEnd) { IStructuredDocumentRegionList newNodeList = getStructuredDocumentRegionList(newStart, newEnd); setupCreationContext(newNodeList.item(0)); insertStructuredDocumentRegions(newNodeList); return; } else { fCreationContext.resetReparseRange(); } } } } /** * */ private CSSNodeImpl insertFontFaceRule(IStructuredDocumentRegion region, IStructuredDocumentRegion braceNode) { CSSNodeImpl parent = fCreationContext.getTargetNode(); if (!fParseFloating && !(parent instanceof ICSSRuleContainer)) { return null; } CSSFontFaceRuleImpl rule = fFeeder.getCSSFontFaceRule(); if (rule == null) { return null; } // setup flat container rule.setRangeStructuredDocumentRegion(region, braceNode); // insert to tree if (!fUpdateContext.isActive() && parent != null) { propagateRangePreInsert(parent, rule); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(rule, next); } else { parent.appendChild(rule); } } fCreationContext.setTargetNode(rule); // TargetNext is set to null automatically return rule; } /** * */ private CSSNodeImpl insertImportRule(IStructuredDocumentRegion beginDocRegion, IStructuredDocumentRegion endDocRegion) { CSSNodeImpl parent = fCreationContext.getTargetNode(); if (!fParseFloating && !(parent instanceof ICSSRuleContainer)) { return null; } ITextRegionList regions = new TextRegionListImpl(beginDocRegion.getRegions()); regions.remove(0); // must be "@import" ITextRegion hrefRegion = null; while (!regions.isEmpty()) { ITextRegion textRegion = regions.remove(0); if (textRegion == null) { continue; } String type = textRegion.getType(); if (type == CSSRegionContexts.CSS_S || type == CSSRegionContexts.CSS_COMMENT) { continue; } if (type == CSSRegionContexts.CSS_URI || type == CSSRegionContexts.CSS_STRING) { hrefRegion = textRegion; break; } else { break; } } if (hrefRegion == null) { return null; } CSSImportRuleImpl rule = fFeeder.getCSSImportRule(); if (rule == null) { return null; } CSSUtil.stripSurroundingSpace(regions); MediaListImpl mediaList = (MediaListImpl) rule.getMedia(); setMediaList(mediaList, beginDocRegion, regions); if (!fUpdateContext.isActive()) { rule.setAttribute(ICSSImportRule.HREF, beginDocRegion.getText(hrefRegion)); } // setup flat container rule.setRangeStructuredDocumentRegion(beginDocRegion, endDocRegion); CSSAttrImpl attr = rule.getAttributeNode(ICSSImportRule.HREF); if (attr != null) { attr.setRangeRegion(beginDocRegion, hrefRegion, hrefRegion); } // insert to tree if (!fUpdateContext.isActive() && parent != null) { propagateRangePreInsert(parent, rule); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(rule, next); } else { parent.appendChild(rule); } } return rule; } /** * */ private CSSNodeImpl insertMediaRule(IStructuredDocumentRegion flatNode, IStructuredDocumentRegion braceNode) { CSSNodeImpl parent = fCreationContext.getTargetNode(); if (!fParseFloating && !(parent instanceof ICSSRuleContainer)) { return null; } CSSMediaRuleImpl rule = fFeeder.getCSSMediaRule(); if (rule == null) { return null; } ITextRegionList regions = new TextRegionListImpl(flatNode.getRegions()); regions.remove(0); // must be "@media" CSSUtil.stripSurroundingSpace(regions); MediaListImpl mediaList = (MediaListImpl) rule.getMedia(); setMediaList(mediaList, flatNode, regions); // setup flat container rule.setRangeStructuredDocumentRegion(flatNode, braceNode); // insert to tree if (!fUpdateContext.isActive() && parent != null) { propagateRangePreInsert(parent, rule); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(rule, next); } else { parent.appendChild(rule); } } fCreationContext.setTargetNode(rule); // TargetNext is set to null automatically return rule; } /** * */ private CSSNodeImpl insertPageRule(IStructuredDocumentRegion flatNode, IStructuredDocumentRegion braceNode) { CSSNodeImpl parent = fCreationContext.getTargetNode(); if (!fParseFloating && !(parent instanceof ICSSRuleContainer)) { return null; } // get selector regions ITextRegionList selectorRegions = new TextRegionListImpl(flatNode.getRegions()); selectorRegions.remove(0); // must be "@page" CSSUtil.stripSurroundingSpace(selectorRegions); CSSPageRuleImpl rule = fFeeder.getCSSPageRule(); if (rule == null) { return null; } if (!fUpdateContext.isActive()) { String selectorStr = CSSUtil.getRegionText(flatNode, selectorRegions); if (0 < selectorStr.length()) { rule.setSelectorText(selectorStr); } } // setup flat container rule.setRangeStructuredDocumentRegion(flatNode, braceNode); CSSAttrImpl attr = rule.getAttributeNode(ICSSPageRule.SELECTOR); if (attr != null && selectorRegions != null && !selectorRegions.isEmpty()) { attr.setRangeRegion(flatNode, selectorRegions.get(0), selectorRegions.get(selectorRegions.size() - 1)); } // insert to tree if (!fUpdateContext.isActive() && parent != null) { propagateRangePreInsert(parent, rule); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(rule, next); } else { parent.appendChild(rule); } } fCreationContext.setTargetNode(rule); // TargetNext is set to null automatically return rule; } /** * */ private CSSNodeImpl insertSemiColonForRule(IStructuredDocumentRegion region) { IStructuredDocumentRegion keyRegion = CSSUtil.findPreviousSignificantNode(region); String type = CSSUtil.getStructuredDocumentRegionType(keyRegion); CSSNodeImpl inserted = null; if (type == CSSRegionContexts.CSS_IMPORT) { inserted = insertImportRule(keyRegion, region); } else if (type == CSSRegionContexts.CSS_CHARSET) { inserted = insertCharsetRule(keyRegion, region); } return inserted; } /** * */ private CSSNodeImpl insertSemiColonForStyleDeclarationItem(IStructuredDocumentRegion region) { // only target/net node is changed. nothing to do. CSSNodeImpl targetNode = fCreationContext.getTargetNode(); if (targetNode instanceof ICSSStyleDeclItem) { int offset = targetNode.getStartOffset(); // widen document region range // ((CSSStyleDeclItemImpl)targetNode).setLastStructuredDocumentRegion(region); CSSModelUtil.expandStructuredDocumentRegionContainer((CSSStyleDeclItemImpl) targetNode, region); // psStructuredDocumentRegion indicates CSSStyleDeclItem ICSSNode parentNode = targetNode.getParentNode(); fCreationContext.setTargetNode(parentNode); ICSSNode next = null; if (parentNode.hasChildNodes()) { for (ICSSNode child = targetNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child instanceof CSSStructuredDocumentRegionContainer && offset < ((CSSStructuredDocumentRegionContainer) child).getStartOffset()) { next = child; break; } } } fCreationContext.setNextNode(next); return targetNode; } return null; } /** * */ private CSSNodeImpl insertStyleDeclarationItem(IStructuredDocumentRegion docRegion) { CSSStyleDeclarationImpl parent = null; CSSNodeImpl node = fCreationContext.getTargetNode(); if (node instanceof CSSRuleDeclContainer) { CSSRuleDeclContainer declContainer = (CSSRuleDeclContainer) node; parent = (CSSStyleDeclarationImpl) declContainer.getStyle(); } else if (node instanceof CSSStyleDeclarationImpl) { parent = (CSSStyleDeclarationImpl) node; } CSSDeclarationItemParser itemParser = new CSSDeclarationItemParser(parent.getOwnerDocument()); itemParser.setStructuredDocumentTemporary(false); itemParser.setUpdateContext(fUpdateContext); CSSStyleDeclItemImpl declItem = itemParser.setupDeclarationItem(docRegion); if (declItem == null) { return null; } // setup flat container declItem.setRangeStructuredDocumentRegion(docRegion, docRegion); // insert to tree if (!fUpdateContext.isActive()) { propagateRangePreInsert(parent, declItem); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(declItem, next); } else { parent.appendChild(declItem); } } fCreationContext.setTargetNode(declItem); // TargetNext is set to null automatically return declItem; } /** * */ private CSSNodeImpl insertStyleRule(IStructuredDocumentRegion flatNode, IStructuredDocumentRegion braceNode) { CSSNodeImpl parent = fCreationContext.getTargetNode(); if (!fParseFloating && !(parent instanceof ICSSRuleContainer)) { return null; } // get selector regions ITextRegionList selectorRegions = new TextRegionListImpl(flatNode.getRegions()); CSSUtil.stripSurroundingSpace(selectorRegions); CSSStyleRuleImpl rule = fFeeder.getCSSStyleRule(); if (rule == null) { return null; } if (!fUpdateContext.isActive()) { String selectorStr = CSSUtil.getRegionText(flatNode, selectorRegions); if (selectorStr != null && 0 < selectorStr.length()) { rule.setSelectorText(selectorStr); } } // setup flat container rule.setRangeStructuredDocumentRegion(flatNode, braceNode); CSSAttrImpl attr = rule.getAttributeNode(ICSSPageRule.SELECTOR); if (attr != null && selectorRegions != null && !selectorRegions.isEmpty()) { attr.setRangeRegion(flatNode, selectorRegions.get(0), selectorRegions.get(selectorRegions.size() - 1)); } // insert to tree if (!fUpdateContext.isActive() && parent != null) { propagateRangePreInsert(parent, rule); CSSNodeImpl next = fCreationContext.getNextNode(); if (next != null) { parent.insertBefore(rule, next); } else { parent.appendChild(rule); } } fCreationContext.setTargetNode(rule); // TargetNext is set to null automatically return rule; } /** * */ private void pretendRemoveNode() { CSSStructuredDocumentRegionContainer node = (CSSStructuredDocumentRegionContainer) fUpdateContext.getDeletionTarget(); if (node == null) { return; } IStructuredDocumentRegion firstNode = node.getFirstStructuredDocumentRegion(); if (firstNode != null) { fDeletionContext.expandRemovedRangeBegin(firstNode); } IStructuredDocumentRegion lastNode = node.getLastStructuredDocumentRegion(); if (lastNode != null) { fDeletionContext.expandRemovedRangeEnd(lastNode); } shrinkContainer((CSSStructuredDocumentRegionContainer) fUpdateContext.getDeletionTargetParent(), node); } /** * currently public but may be made default access protected in future. * * @param parent * org.eclipse.wst.css.core.model.CSSNodeImpl * @param child * org.eclipse.wst.css.core.model.CSSNodeImpl */ protected void propagateRangePreInsert(CSSNodeImpl parent, CSSNodeImpl child) { if (!(child instanceof CSSStructuredDocumentRegionContainer) || !(parent instanceof CSSStructuredDocumentRegionContainer)) { return; } CSSStructuredDocumentRegionContainer parentContainer = (CSSStructuredDocumentRegionContainer) parent; CSSStructuredDocumentRegionContainer childContainer = (CSSStructuredDocumentRegionContainer) child; IStructuredDocumentRegion firstNode = childContainer.getFirstStructuredDocumentRegion(); IStructuredDocumentRegion lastNode = childContainer.getLastStructuredDocumentRegion(); if (firstNode == null || lastNode == null) { return; } boolean bModified = parentContainer.includeRangeStructuredDocumentRegion(firstNode, lastNode); if (bModified) { parentContainer.propagateRangeStructuredDocumentRegion(); } } /** * */ private void removeStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { String type = CSSUtil.getStructuredDocumentRegionType(flatNode); if (type == CSSRegionContexts.CSS_DELIMITER || type == CSSRegionContexts.CSS_DECLARATION_DELIMITER) { do { flatNode = fStructuredDocumentWalker.getPrevNode(flatNode); type = (flatNode != null) ? CSSUtil.getStructuredDocumentRegionType(flatNode) : null; } while (type != null && (type == CSSRegionContexts.CSS_S || type == CSSRegionContexts.CSS_COMMENT)); } if (flatNode == null) { return; } // if (fDeletionContext.isInRemovedRange(flatNode)) { // already // removed // return; // } CSSStructuredDocumentRegionContainer node = fDeletionContext.findDeletionTarget((CSSNodeImpl)fDocument, flatNode); if (node == null || node == fDocument) { return; // not attached with any treeNode } if (node instanceof CSSStyleDeclarationImpl) { ICSSNode rule = node.getParentNode(); if (rule instanceof CSSStyleRuleImpl) { node = (CSSStructuredDocumentRegionContainer) rule; } else { return; } } // ICSSNode p = node.getParentNode(); // if (p == null || ! (p instanceof // CSSStructuredDocumentRegionContainer)) { // return; // } // CSSStructuredDocumentRegionContainer parent = // (CSSStructuredDocumentRegionContainer)p; if (fDeletionContext.addNodeToBeRemoved(node)) { IStructuredDocumentRegion firstNode = node.getFirstStructuredDocumentRegion(); if (firstNode != null) { fDeletionContext.expandRemovedRangeBegin(firstNode); } IStructuredDocumentRegion lastNode = node.getLastStructuredDocumentRegion(); if (lastNode != null) { fDeletionContext.expandRemovedRangeEnd(lastNode); } } // shrinkContainer(node); // parent.removeChild(node); } /** * */ private void removeStructuredDocumentRegions(IStructuredDocumentRegionList flatNodes) { for (Enumeration e = flatNodes.elements(); e.hasMoreElements();) { IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); if (flatNode == null) { continue; } removeStructuredDocumentRegion(flatNode); } Iterator i = fDeletionContext.getNodesToBeRemoved(); while (i.hasNext()) { CSSNodeImpl node = (CSSNodeImpl) i.next(); if (!(node instanceof CSSStructuredDocumentRegionContainer)) { continue; } CSSNodeImpl parent = (CSSNodeImpl) node.getParentNode(); if (!(parent instanceof CSSStructuredDocumentRegionContainer)) { continue; } shrinkContainer((CSSStructuredDocumentRegionContainer) parent, (CSSStructuredDocumentRegionContainer) node); parent.removeChild(node); } } void replaceDocument(IStructuredDocumentRegionList newStructuredDocumentRegions) { if (fDocument == null || fCreationContext == null) return; ((CSSDocumentImpl) fDocument).removeChildNodes(); if (newStructuredDocumentRegions != null && 0 < newStructuredDocumentRegions.getLength()) { setupCreationContext(newStructuredDocumentRegions.item(0)); insertStructuredDocumentRegions(newStructuredDocumentRegions); } IStructuredDocument structuredDocument = fStructuredDocumentWalker.getStructuredDocument(); ((CSSStructuredDocumentRegionContainer)fDocument).setRangeStructuredDocumentRegion(structuredDocument.getFirstStructuredDocumentRegion(), structuredDocument.getLastStructuredDocumentRegion()); } /** * */ void replaceStructuredDocumentRegions(IStructuredDocumentRegionList newStructuredDocumentRegions, IStructuredDocumentRegionList oldStructuredDocumentRegions) { if (fDocument == null || fCreationContext == null) { return; } if (oldStructuredDocumentRegions != null && 0 < oldStructuredDocumentRegions.getLength()) { setupDeletionContext(newStructuredDocumentRegions, oldStructuredDocumentRegions); short updateMode = fUpdateContext.getUpdateMode(); if (updateMode == CSSModelUpdateContext.UPDATE_IDLE) { removeStructuredDocumentRegions(oldStructuredDocumentRegions); } else { pretendRemoveNode(); } newStructuredDocumentRegions = getStructuredDocumentRegionList(fDeletionContext.getRemovedRangeBegin(), fDeletionContext.getRemovedRangeEnd()); cleanupDeletionContext(); } if (newStructuredDocumentRegions != null && 0 < newStructuredDocumentRegions.getLength()) { /* when removing old nodes the creation context should be set up based on them * else creation context is that of the new nodes */ if( oldStructuredDocumentRegions != null && oldStructuredDocumentRegions.getLength() < 0) { setupCreationContext(oldStructuredDocumentRegions.item(0)); } else { setupCreationContext(newStructuredDocumentRegions.item(0)); } insertStructuredDocumentRegions(newStructuredDocumentRegions); } // make document hold whole structuredDocument /* * boolean bUpdate = false; IStructuredDocumentRegion flatNode; * flatNode = fDocument.getFirstStructuredDocumentRegion(); bUpdate = * bUpdate || (flatNode == null || * fStructuredDocumentWalker.isOldNode(flatNode) || * flatNode.getPrevious() != null); flatNode = * fDocument.getLastStructuredDocumentRegion(); bUpdate = bUpdate || * (flatNode == null || fStructuredDocumentWalker.isOldNode(flatNode) || * flatNode.getNext() != null); */ IStructuredDocument structuredDocument = fStructuredDocumentWalker.getStructuredDocument(); ((CSSStructuredDocumentRegionContainer)fDocument).setRangeStructuredDocumentRegion(structuredDocument.getFirstStructuredDocumentRegion(), structuredDocument.getLastStructuredDocumentRegion()); /* } */ // remove in official release // CSSModelUtil.diagnoseTree(fDocument, // fStructuredDocumentWalker.getStructuredDocument()); } /** * */ void replaceRegions(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { if (flatNode == null) { return; } if (newRegions == null || oldRegions == null) { return; } if (fDocument == null) { return; } changeStructuredDocumentRegion(flatNode); } /** */ private void resetCreationTarget(IStructuredDocumentRegion newStructuredDocumentRegion) { if (newStructuredDocumentRegion == null || newStructuredDocumentRegion.getStartOffset() <= 0) { // top of document fCreationContext.setTargetNode(fDocument); fCreationContext.setNextNode(fDocument.getFirstChild()); return; } int cursorPos = newStructuredDocumentRegion.getStartOffset(); CSSNodeImpl cursorNode = getNodeAt(cursorPos); if (cursorNode == null) { // end of document cursorNode = (CSSNodeImpl)fDocument; } // find edge of tree node CSSNodeImpl node = null; // boolean bOverSemiColon = false; boolean bOverOpenBrace = false; IStructuredDocumentRegion flatNode; for (flatNode = newStructuredDocumentRegion; flatNode != null; flatNode = flatNode.getPrevious()) { node = getNodeAt(flatNode.getStartOffset()); if (node == null) { node = (CSSNodeImpl)fDocument; } if (node != cursorNode || node.getStartOffset() == flatNode.getStartOffset()) { break; } if (flatNode != newStructuredDocumentRegion) { String type = CSSUtil.getStructuredDocumentRegionType(flatNode); // if (type == CSSRegionContexts.CSS_DELIMITER || // type == CSSRegionContexts.CSS_DECLARATION_DELIMITER) { // bOverSemiColon = true; // } else if (type == CSSRegionContexts.CSS_LBRACE) { bOverOpenBrace = true; } } } CSSNodeImpl targetNode = null; // if (flatNode == null) { // v<--| // AAAAAA // BBBBBBBBBB cursorNode:A , node:B -> target is A // targetNode = (node == null) ? fDocument : node; // } else if (cursorNode == node) { // v<--| // AAAAAA // BBBBBBBBBB cursorNode:A , node:B -> target is A if (bOverOpenBrace && cursorNode instanceof CSSRuleDeclContainer) { targetNode = (CSSNodeImpl) ((CSSRuleDeclContainer) cursorNode).getStyle(); } else { targetNode = cursorNode; } } else { // 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) { // targetNode = (CSSNodeImpl)((bOverSemiColon) ? // node.getParentNode() : node); // NP String regionType = CSSUtil.getStructuredDocumentRegionType(flatNode); if (regionType == CSSRegionContexts.CSS_DELIMITER || regionType == CSSRegionContexts.CSS_DECLARATION_DELIMITER) { targetNode = (CSSNodeImpl) node.getParentNode(); } else { targetNode = node; } } else if (CSSUtil.getStructuredDocumentRegionType(flatNode) == CSSRegionContexts.CSS_RBRACE) { targetNode = (CSSNodeImpl) node.getParentNode(); } else { targetNode = node; } } fCreationContext.setTargetNode(targetNode); ICSSNode next = null; if (targetNode.hasChildNodes()) { for (ICSSNode child = targetNode.getFirstChild(); child != null; child = child.getNextSibling()) { if (child instanceof CSSStructuredDocumentRegionContainer && cursorPos < ((CSSStructuredDocumentRegionContainer) child).getStartOffset()) { next = child; break; } } } fCreationContext.setNextNode(next); } /** */ private void resetReparseRange() { fCreationContext.clear(); } void setStructuredDocumentEvent(StructuredDocumentEvent event) { if (fStructuredDocumentWalker == null) { fStructuredDocumentWalker = new StructuredDocumentWalker(); } fStructuredDocumentWalker.initialize(event); } /** * regions: surrounding spaces should be removed. Q. Why did you set * mediaTable ? A. MediaList may have two or more medium that have same * value, then searcing in MediaList is not perfect. Q. * fUpdateContext.isActive() is not care. Are you OK? A. OK. */ private void setMediaList(MediaListImpl mediaList, IStructuredDocumentRegion region, ITextRegionList textRegions) { if (mediaList == null || textRegions == null) { return; } Collection mediaTable = new HashSet(); CSSNodeListImpl attrs = mediaList.getMedia(); for (int i = 0; i != attrs.getLength(); i++) { mediaTable.add(attrs.item(i)); } ITextRegion start = null; ITextRegion end = null; Iterator i = textRegions.iterator(); ITextRegion textRegion = (ITextRegion) ((i.hasNext()) ? i.next() : null); while (textRegion != null) { if (textRegion.getType() == CSSRegionContexts.CSS_MEDIUM) { String mediumStr = region.getText(textRegion); if (0 < mediumStr.length()) { CSSAttrImpl attr = null; // is the medium already set ? Iterator iTable = mediaTable.iterator(); while (iTable.hasNext()) { CSSAttrImpl cai = (CSSAttrImpl) iTable.next(); if (mediumStr.equalsIgnoreCase(cai.getValue())) { attr = cai; mediaTable.remove(cai); break; } } if (attr == null) { // is not set. create new attribute String key = "mediumP" + mediaList.mediumCounter++; //$NON-NLS-1$ mediaList.setAttribute(key, mediumStr); attr = mediaList.getAttributeNode(key); } attr.setRangeRegion(region, textRegion, textRegion); if (start == null) { start = textRegion; } end = textRegion; } } textRegion = (ITextRegion) ((i.hasNext()) ? i.next() : null); } if (start != null && end != null) { mediaList.setRangeRegion(region, start, end); } } /** * */ private void setupCreationContext(IStructuredDocumentRegion region) { resetReparseRange(); resetCreationTarget(region); } /** * */ private void setupDeletionContext(IStructuredDocumentRegionList newStructuredDocumentRegions, IStructuredDocumentRegionList oldStructuredDocumentRegions) { fDeletionContext.setupContext(newStructuredDocumentRegions, oldStructuredDocumentRegions); } /** * */ void setupUpdateContext(short updateMode, ICSSNode parentNode, ICSSNode targetNode) { fUpdateContext.setupContext(updateMode, parentNode, targetNode); } /** * */ void shrinkContainer(CSSStructuredDocumentRegionContainer parent, CSSStructuredDocumentRegionContainer child) { if (child == null) { return; } boolean bModified = false; bModified = bModified || cleanupLastNode(child.getLastStructuredDocumentRegion(), parent); bModified = bModified || cleanupFirstNode(child.getFirstStructuredDocumentRegion(), parent); if (bModified) { if (parent != null) { for (ICSSNode node = parent.getFirstChild(); node != null; node = node.getNextSibling()) { if (child != node && node instanceof CSSStructuredDocumentRegionContainer) { ((CSSStructuredDocumentRegionContainer) node).propagateRangeStructuredDocumentRegion(); } } } } child.setRangeStructuredDocumentRegion(null, null); } }