| /******************************************************************************* |
| * 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.formatter; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| |
| import org.eclipse.core.runtime.Preferences; |
| import org.eclipse.jface.text.DefaultLineTracker; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.wst.css.core.internal.CSSCorePlugin; |
| import org.eclipse.wst.css.core.internal.cleanup.CSSCleanupStrategy; |
| import org.eclipse.wst.css.core.internal.cleanup.CSSCleanupStrategyImpl; |
| import org.eclipse.wst.css.core.internal.parserz.CSSRegionContexts; |
| import org.eclipse.wst.css.core.internal.preferences.CSSCorePreferenceNames; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSAttr; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSDocument; |
| 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.ICSSStyleDeclItem; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSStyleDeclaration; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSStyleSheet; |
| import org.eclipse.wst.css.core.internal.util.CSSLinkConverter; |
| import org.eclipse.wst.css.core.internal.util.CSSUtil; |
| import org.eclipse.wst.css.core.internal.util.RegionIterator; |
| import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| 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.ITextRegion; |
| import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; |
| import org.eclipse.wst.sse.core.internal.util.Assert; |
| |
| /** |
| * |
| */ |
| public abstract class AbstractCSSSourceFormatter implements CSSSourceGenerator { |
| |
| protected final static short GENERATE = 0; |
| protected final static short FORMAT = 1; |
| protected final static short CLEANUP = 2; |
| protected static short strategy; |
| |
| /** |
| * |
| */ |
| AbstractCSSSourceFormatter() { |
| super(); |
| } |
| |
| /** |
| * |
| */ |
| protected void appendDelimBefore(ICSSNode node, CompoundRegion toAppend, StringBuffer source) { |
| if (node == null || source == null) |
| return; |
| if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) |
| return; // for not formatting case on cleanup action |
| String delim = getLineDelimiter(node); |
| |
| boolean needIndent = !(node instanceof ICSSStyleSheet); |
| if (toAppend == null) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else { |
| String type = toAppend.getType(); |
| if (type == CSSRegionContexts.CSS_COMMENT) { |
| RegionIterator it = new RegionIterator(toAppend.getDocumentRegion(), toAppend.getTextRegion()); |
| it.prev(); |
| ITextRegion prev = it.prev(); |
| if (prev == null || (prev.getType() == CSSRegionContexts.CSS_S && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, it.getStructuredDocumentRegion().getText(prev), 0)[0] >= 0)) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else { |
| appendSpaceBefore(node, toAppend.getText(), source); |
| } |
| } else if (type == CSSRegionContexts.CSS_DELIMITER || type == CSSRegionContexts.CSS_DECLARATION_DELIMITER) { |
| RegionIterator it = new RegionIterator(toAppend.getDocumentRegion(), toAppend.getTextRegion()); |
| it.prev(); |
| ITextRegion prev = it.prev(); |
| |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| |
| if (prev.getType() == CSSRegionContexts.CSS_S && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, it.getStructuredDocumentRegion().getText(prev), 0)[0] >= 0) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else if (preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH) > 0 && (!preferences.getBoolean(CSSCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node.getOwnerDocument().getNodeType() != ICSSNode.STYLEDECLARATION_NODE)) { |
| int length = getLastLineLength(node, source); |
| int append = 1; |
| if (length + append > preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } |
| } else if (type == CSSRegionContexts.CSS_RBRACE || type == CSSRegionContexts.CSS_LBRACE) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| } else { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| protected void appendSpaceBefore(ICSSNode node, CompoundRegion toAppend, StringBuffer source) { |
| if (node == null || toAppend == null || source == null) |
| return; |
| if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) |
| return; // for not formatting case on cleanup action |
| String type = toAppend.getType(); |
| |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| |
| boolean needIndent = !(node instanceof ICSSStyleSheet); |
| if (type == CSSRegionContexts.CSS_COMMENT) { |
| // check whether previous region is 'S' and has CR-LF |
| String delim = getLineDelimiter(node); |
| RegionIterator it = new RegionIterator(toAppend.getDocumentRegion(), toAppend.getTextRegion()); |
| it.prev(); |
| ITextRegion prev = it.prev(); |
| if (prev.getType() == CSSRegionContexts.CSS_S && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, it.getStructuredDocumentRegion().getText(prev), 0)[0] >= 0) { |
| source.append(delim); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } else { |
| appendSpaceBefore(node, toAppend.getText(), source); |
| } |
| } else if (type == CSSRegionContexts.CSS_LBRACE && preferences.getBoolean(CSSCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE)) { |
| String delim = getLineDelimiter(node); |
| source.append(delim); |
| source.append(getIndent(node)); |
| // } else if (type == CSSRegionContexts.CSS_CURLY_BRACE_CLOSE) { |
| // } else if (type == CSSRegionContexts.CSS_INCLUDES || type == |
| // CSSRegionContexts.CSS_DASHMATCH) { |
| } else if (type == CSSRegionContexts.CSS_DECLARATION_SEPARATOR && node instanceof ICSSStyleDeclItem) { |
| int n = preferences.getInt(CSSCorePreferenceNames.FORMAT_PROP_PRE_DELIM); |
| if (preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH) > 0 && (!preferences.getBoolean(CSSCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node.getOwnerDocument().getNodeType() != ICSSNode.STYLEDECLARATION_NODE)) { |
| int length = getLastLineLength(node, source); |
| int append = 1; |
| if (length + n + append > preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| n = 0; // no space is necessary |
| } |
| } |
| // no delimiter case |
| while (n-- > 0) |
| source.append(" ");//$NON-NLS-1$ |
| } else if (type == CSSRegionContexts.CSS_DECLARATION_DELIMITER || type == CSSRegionContexts.CSS_DECLARATION_VALUE_OPERATOR || type == CSSRegionContexts.CSS_DECLARATION_VALUE_PARENTHESIS_CLOSE) { |
| if (preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH) > 0 && (!preferences.getBoolean(CSSCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node.getOwnerDocument().getNodeType() != ICSSNode.STYLEDECLARATION_NODE)) { |
| int length = getLastLineLength(node, source); |
| int append = 1; |
| if (length + append > preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| if (needIndent) |
| source.append(getIndentString()); |
| } |
| } |
| } else |
| appendSpaceBefore(node, toAppend.getText(), source); |
| } |
| |
| /** |
| * |
| */ |
| protected void appendSpaceBefore(ICSSNode node, String toAppend, StringBuffer source) { |
| if (node == null || source == null) |
| return; |
| if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) |
| return; // for not formatting case on cleanup action |
| |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| if (toAppend != null && toAppend.startsWith("{") && preferences.getBoolean(CSSCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE)) {//$NON-NLS-1$ |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| return; |
| } else if (/* ! mgr.isOnePropertyPerLine() && */preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH) > 0 && (!preferences.getBoolean(CSSCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node.getOwnerDocument().getNodeType() != ICSSNode.STYLEDECLARATION_NODE)) { |
| int n = getLastLineLength(node, source); |
| int append = (toAppend != null) ? TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, toAppend, 0)[0] : 0; |
| if (toAppend != null) |
| append = (append < 0) ? toAppend.length() : append; |
| if (n + append + 1 > preferences.getInt(CSSCorePreferenceNames.LINE_WIDTH)) { |
| source.append(getLineDelimiter(node)); |
| source.append(getIndent(node)); |
| source.append(getIndentString()); |
| return; |
| } |
| } |
| source.append(" ");//$NON-NLS-1$ |
| } |
| |
| /** |
| * |
| */ |
| public final StringBuffer cleanup(ICSSNode node) { |
| short oldStrategy = strategy; |
| strategy = CLEANUP; |
| StringBuffer source = formatProc(node); |
| strategy = oldStrategy; |
| |
| return source; |
| } |
| |
| /** |
| * |
| */ |
| public final StringBuffer cleanup(ICSSNode node, IRegion region) { |
| short oldStrategy = strategy; |
| strategy = CLEANUP; |
| StringBuffer source = formatProc(node, region); |
| strategy = oldStrategy; |
| |
| return source; |
| } |
| |
| /** |
| * |
| */ |
| protected String decoratedIdentRegion(CompoundRegion region, CSSCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| String text = region.getText(); |
| if (region.getType() == CSSRegionContexts.CSS_STRING || region.getType() == CSSRegionContexts.CSS_URI) |
| return decoratedRegion(region, 0, stgy); |
| |
| if (isCleanup()) { |
| if (stgy.getIdentCase() == CSSCleanupStrategy.ASIS || region.getType() == CSSRegionContexts.CSS_COMMENT) |
| return text; |
| else if (stgy.getIdentCase() == CSSCleanupStrategy.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| if (region.getType() == CSSRegionContexts.CSS_COMMENT) |
| return text; |
| else if (preferences.getInt(CSSCorePreferenceNames.CASE_IDENTIFIER) == CSSCorePreferenceNames.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| |
| /** |
| * |
| */ |
| protected String decoratedPropNameRegion(CompoundRegion region, CSSCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| String text = region.getText(); |
| if (region.getType() == CSSRegionContexts.CSS_STRING || region.getType() == CSSRegionContexts.CSS_URI) |
| return decoratedRegion(region, 1, stgy); |
| if (isCleanup()) { |
| if (stgy.getPropNameCase() == CSSCleanupStrategy.ASIS || region.getType() != CSSRegionContexts.CSS_DECLARATION_PROPERTY) |
| return text; |
| else if (stgy.getPropNameCase() == CSSCleanupStrategy.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| |
| if (region.getType() != CSSRegionContexts.CSS_DECLARATION_PROPERTY) |
| return text; |
| else if (preferences.getInt(CSSCorePreferenceNames.CASE_PROPERTY_NAME) == CSSCorePreferenceNames.UPPER) |
| return text.toUpperCase(); |
| else |
| return text.toLowerCase(); |
| } |
| |
| /** |
| * |
| */ |
| protected String decoratedPropValueRegion(CompoundRegion region, CSSCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| String text = region.getText(); |
| String type = region.getType(); |
| if (type == CSSRegionContexts.CSS_STRING || type == CSSRegionContexts.CSS_URI) |
| return decoratedRegion(region, 2, stgy); |
| if (isCleanup()) { |
| if (stgy.getPropValueCase() != CSSCleanupStrategy.ASIS) { |
| if (type == CSSRegionContexts.CSS_COMMENT) { |
| } else { |
| if (stgy.getPropValueCase() == CSSCleanupStrategy.UPPER) |
| text = text.toUpperCase(); |
| else |
| text = text.toLowerCase(); |
| } |
| } |
| } |
| return text; |
| } |
| |
| /** |
| * |
| */ |
| protected String decoratedRegion(CompoundRegion region, int type, CSSCleanupStrategy stgy) { |
| if (isFormat()) |
| return region.getText(); |
| |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| String text = region.getText(); |
| if (region.getType() == CSSRegionContexts.CSS_URI) { |
| String uri = CSSLinkConverter.stripFunc(text); |
| |
| boolean prefIsUpper = preferences.getInt(CSSCorePreferenceNames.CASE_IDENTIFIER) == CSSCorePreferenceNames.UPPER; |
| boolean upper = (type == 0) ? prefIsUpper : ((type == 1) ? preferences.getInt(CSSCorePreferenceNames.CASE_PROPERTY_NAME) == CSSCorePreferenceNames.UPPER : preferences.getInt(CSSCorePreferenceNames.CASE_PROPERTY_VALUE) == CSSCorePreferenceNames.UPPER); |
| String func = text.substring(0, 4); |
| if (isCleanup()) { |
| upper = ((type == 0) ? stgy.getIdentCase() : ((type == 1) ? stgy.getPropNameCase() : stgy.getPropValueCase())) == CSSCleanupStrategy.UPPER; |
| func = ((type == 0) ? stgy.getIdentCase() : ((type == 1) ? stgy.getPropNameCase() : stgy.getPropValueCase())) == CSSCleanupStrategy.ASIS ? text.substring(0, 4) : (upper ? "URL(" : "url(");//$NON-NLS-2$//$NON-NLS-1$ |
| } |
| if ((!isCleanup() && preferences.getBoolean(CSSCorePreferenceNames.FORMAT_QUOTE_IN_URI)) || (isCleanup() && stgy.isQuoteValues())) { |
| String quote = preferences.getString(CSSCorePreferenceNames.FORMAT_QUOTE); |
| quote = CSSUtil.detectQuote(uri, quote); |
| text = func + quote + uri + quote + ")";//$NON-NLS-1$ |
| } else if (isCleanup() && !stgy.isQuoteValues()) { |
| text = func + CSSLinkConverter.removeFunc(text) + ")";//$NON-NLS-1$ |
| } else { |
| text = func + uri + ")";//$NON-NLS-1$ |
| } |
| } else if (region.getType() == CSSRegionContexts.CSS_STRING && (!isCleanup() || stgy.isQuoteValues())) { |
| String quote = preferences.getString(CSSCorePreferenceNames.FORMAT_QUOTE); |
| // begginning |
| if (!text.startsWith(quote)) { |
| if (text.startsWith("\"") || text.startsWith("\'")) //$NON-NLS-1$ //$NON-NLS-2$ |
| text = quote + text.substring(1); |
| else |
| text = quote + text; |
| } |
| // ending |
| if (!text.endsWith(quote)) { |
| if (text.endsWith("\"") || text.endsWith("\'")) //$NON-NLS-1$ //$NON-NLS-2$ |
| text = text.substring(0, text.length() - 1) + quote; |
| else |
| text = text + quote; |
| } |
| } |
| return text; |
| } |
| |
| /** |
| * |
| */ |
| public final StringBuffer format(ICSSNode node) { |
| short oldStrategy = strategy; |
| strategy = FORMAT; |
| StringBuffer source = formatProc(node); |
| strategy = oldStrategy; |
| |
| return source; |
| } |
| |
| /** |
| * |
| */ |
| public final StringBuffer format(ICSSNode node, IRegion region) { |
| short oldStrategy = strategy; |
| strategy = FORMAT; |
| StringBuffer source = formatProc(node, region); |
| strategy = oldStrategy; |
| |
| return source; |
| } |
| |
| /** |
| * |
| */ |
| public StringBuffer formatAttrChanged(ICSSNode node, ICSSAttr attr, boolean insert, AttrChangeContext context) { |
| return new StringBuffer(insert && (attr != null) ? attr.getValue() : "");//$NON-NLS-1$ |
| } |
| |
| /** |
| * Generate or format source between children('child' and its previous |
| * sibling) and append to string buffer |
| */ |
| abstract protected void formatBefore(ICSSNode node, ICSSNode child, String toAppend, StringBuffer source, IRegion exceptFor); |
| |
| /** |
| * Generate or format source between children('child' and its previous |
| * sibling) and append to string buffer |
| */ |
| public final StringBuffer formatBefore(ICSSNode node, ICSSNode child, IRegion exceptFor) { |
| Assert.isTrue(child == null || child.getParentNode() == node); |
| StringBuffer buf = new StringBuffer(); |
| formatBefore(node, child, /* (child != null) ? (child.getCssText()) : */"", buf, exceptFor);//$NON-NLS-1$ |
| return buf; |
| } |
| |
| /** |
| * Generate or format source between children('child' and its previous |
| * sibling) and append to string buffer |
| */ |
| protected abstract void formatBefore(ICSSNode node, ICSSNode child, IRegion region, String toAppend, StringBuffer source); |
| |
| /** |
| * |
| */ |
| protected final void formatChildren(ICSSNode node, StringBuffer source) { |
| ICSSNode child = node.getFirstChild(); |
| boolean first = true; |
| while (child != null) { |
| // append child |
| CSSSourceFormatter formatter = (CSSSourceFormatter) ((INodeNotifier) child).getAdapterFor(CSSSourceFormatter.class); |
| if (formatter == null) { |
| formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter((INodeNotifier) child); |
| } |
| StringBuffer childSource = ((AbstractCSSSourceFormatter) formatter).formatProc(child); |
| if (!first) { |
| formatBefore(node, child, new String(childSource), source, null); |
| } |
| source.append(childSource); |
| // append between children |
| child = child.getNextSibling(); |
| first = false; |
| } |
| } |
| |
| /** |
| * |
| */ |
| protected final void formatChildren(ICSSNode node, IRegion region, StringBuffer source) { |
| ICSSNode child = node.getFirstChild(); |
| int start = region.getOffset(); |
| int end = region.getOffset() + region.getLength(); |
| boolean first = true; |
| while (child != null) { |
| int curEnd = ((IndexedRegion) child).getEndOffset(); |
| StringBuffer childSource = null; |
| boolean toFinish = false; |
| if (start < curEnd) { |
| int curStart = ((IndexedRegion) child).getStartOffset(); |
| if (curStart < end) { |
| // append child |
| CSSSourceFormatter formatter = (CSSSourceFormatter) ((INodeNotifier) child).getAdapterFor(CSSSourceFormatter.class); |
| if (formatter == null) { |
| formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter((INodeNotifier) child); |
| } |
| if (includes(region, curStart, curEnd)) |
| childSource = ((AbstractCSSSourceFormatter) formatter).formatProc(child); |
| else |
| childSource = ((AbstractCSSSourceFormatter) formatter).formatProc(child, overlappedRegion(region, curStart, curEnd)); |
| } else |
| toFinish = true; |
| } |
| // append between children |
| if (!first) { |
| curEnd = ((IndexedRegion) child).getStartOffset(); // change |
| // only |
| // start |
| if (start < curEnd) { |
| int curStart = ((IndexedRegion) child.getPreviousSibling()).getEndOffset(); |
| if (curStart < end) { |
| String toAppend = (childSource != null) ? new String(childSource) : "";//$NON-NLS-1$ |
| if (includes(region, curStart, curEnd)) |
| formatBefore(node, child, toAppend, source, null); |
| else |
| formatBefore(node, child, overlappedRegion(region, curStart, curEnd), toAppend, source); |
| } |
| } |
| } |
| if (childSource != null) { |
| source.append(childSource); |
| } |
| first = false; |
| if (toFinish) |
| break; |
| child = child.getNextSibling(); |
| } |
| } |
| |
| /** |
| * Generate or format source after the last child and append to string |
| * buffer |
| */ |
| protected abstract void formatPost(ICSSNode node, StringBuffer source); |
| |
| /** |
| * Generate or format source after the last child and append to string |
| * buffer |
| */ |
| protected abstract void formatPost(ICSSNode node, IRegion region, StringBuffer source); |
| |
| /** |
| * Generate or format source before the first child and append to string |
| * buffer |
| */ |
| protected abstract void formatPre(ICSSNode node, StringBuffer source); |
| |
| /** |
| * Generate or format source before the first child and append to string |
| * buffer |
| */ |
| abstract protected void formatPre(ICSSNode node, IRegion region, StringBuffer source); |
| |
| /** |
| * |
| * @return java.lang.StringBuffer |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.ICSSNode |
| */ |
| protected final StringBuffer formatProc(ICSSNode node) { |
| StringBuffer source = new StringBuffer(); |
| formatPre(node, source); |
| formatChildren(node, source); |
| formatPost(node, source); |
| return source; |
| } |
| |
| /** |
| * |
| * @return java.lang.StringBuffer |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.ICSSNode |
| * @param region |
| * org.eclipse.jface.text.IRegion |
| */ |
| protected final StringBuffer formatProc(ICSSNode node, IRegion region) { |
| StringBuffer source = new StringBuffer(); |
| int curStart = ((IndexedRegion) node).getStartOffset(); |
| int curEnd = ((IndexedRegion) node).getEndOffset(); |
| if (node.getChildNodes().getLength() > 0) { |
| curEnd = ((IndexedRegion) node.getFirstChild()).getStartOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPre(node, source); |
| else |
| formatPre(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| curStart = curEnd; |
| curEnd = ((IndexedRegion) node.getLastChild()).getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatChildren(node, source); |
| else |
| formatChildren(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| curStart = curEnd; |
| curEnd = ((IndexedRegion) node).getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPost(node, source); |
| else |
| formatPost(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| } else { |
| curEnd = getChildInsertPos(node); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPre(node, source); |
| else |
| formatPre(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| curStart = curEnd; |
| curEnd = ((IndexedRegion) node).getEndOffset(); |
| if (overlaps(region, curStart, curEnd)) { |
| if (includes(region, curStart, curEnd)) |
| formatPost(node, source); |
| else |
| formatPost(node, overlappedRegion(region, curStart, curEnd), source); |
| } |
| } |
| return source; |
| } |
| |
| /** |
| * |
| */ |
| public int getAttrInsertPos(ICSSNode node, String attrName) { |
| return -1; |
| } |
| |
| /** |
| * Insert the method's description here. |
| * |
| * @return org.eclipse.wst.css.core.internal.cleanup.CSSCleanupStrategy |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.ICSSNode |
| */ |
| protected CSSCleanupStrategy getCleanupStrategy(ICSSNode node) { |
| CSSCleanupStrategy currentStrategy = CSSCleanupStrategyImpl.getInstance(); |
| ICSSDocument doc = node.getOwnerDocument(); |
| if (doc == null) |
| return currentStrategy; |
| ICSSModel model = doc.getModel(); |
| if (model == null) |
| return currentStrategy; |
| if (model.getStyleSheetType() != ICSSModel.EXTERNAL) { |
| // TODO - TRANSITION Nakamori-san, or Kit, how can we move to |
| // "HTML" plugin? |
| // can we subclass? |
| // currentStrategy = CSSInHTMLCleanupStrategyImpl.getInstance(); |
| } |
| return currentStrategy; |
| } |
| |
| /** |
| * |
| */ |
| protected String getIndent(ICSSNode node) { |
| if (node == null) |
| return "";//$NON-NLS-1$ |
| ICSSNode parent = node.getParentNode(); |
| if (node instanceof ICSSAttr) |
| parent = ((ICSSAttr) node).getOwnerCSSNode(); |
| if (parent == null) |
| return "";//$NON-NLS-1$ |
| if (node instanceof org.w3c.dom.css.CSSStyleDeclaration) |
| parent = parent.getParentNode(); |
| if (parent == null) |
| return "";//$NON-NLS-1$ |
| |
| String parentIndent = getIndent(parent); |
| if (parent instanceof org.w3c.dom.css.CSSRule) |
| return parentIndent + getIndentString(); |
| if (node.getParentNode() instanceof ICSSStyleDeclaration) |
| return parentIndent + getIndentString(); |
| return parentIndent; |
| } |
| |
| /** |
| * |
| */ |
| protected int getLastLineLength(ICSSNode node, StringBuffer source) { |
| if (node == null || source == null) |
| return 0; |
| String delim = getLineDelimiter(node); |
| String str = new String(source); |
| int n = str.lastIndexOf(delim); |
| if (n < 0) |
| return str.length(); |
| |
| return str.length() - n - delim.length(); |
| } |
| |
| /** |
| * |
| * @return int |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.ICSSNode |
| * @param insertPos |
| * int |
| */ |
| public int getLengthToReformatAfter(ICSSNode node, int insertPos) { |
| if (node == null) |
| return 0; |
| IndexedRegion nnode = (IndexedRegion) node; |
| if (insertPos < 0 || !nnode.contains(insertPos)) |
| return 0; |
| |
| IStructuredDocumentRegion flatNode = node.getOwnerDocument().getModel().getStructuredDocument().getRegionAtCharacterOffset(insertPos); |
| if (flatNode == null) |
| return 0; |
| ITextRegion region = flatNode.getRegionAtCharacterOffset(insertPos); |
| if (region == null) |
| return 0; |
| RegionIterator it = new RegionIterator(flatNode, region); |
| boolean found = false; |
| while (it.hasNext()) { |
| region = it.next(); |
| // if (region.getType() != CSSRegionContexts.CSS_S && |
| // region.getType() != CSSRegionContexts.CSS_DELIMITER && |
| // region.getType() != |
| // CSSRegionContexts.CSS_DECLARATION_DELIMITER) { |
| if (region.getType() != CSSRegionContexts.CSS_S) { |
| found = true; |
| break; |
| } |
| } |
| int pos = (found ? it.getStructuredDocumentRegion().getStartOffset(region) : it.getStructuredDocumentRegion().getTextEndOffset(region)) - insertPos; |
| return (pos >= 0) ? pos : 0; |
| } |
| |
| /** |
| * |
| * @return int |
| * @param node |
| * org.eclipse.wst.css.core.model.interfaces.ICSSNode |
| * @param insertPos |
| * int |
| */ |
| public int getLengthToReformatBefore(ICSSNode node, int insertPos) { |
| if (node == null) |
| return 0; |
| IndexedRegion nnode = (IndexedRegion) node; |
| if (insertPos <= 0 || !nnode.contains(insertPos - 1)) |
| return 0; |
| |
| IStructuredDocumentRegion flatNode = node.getOwnerDocument().getModel().getStructuredDocument().getRegionAtCharacterOffset(insertPos - 1); |
| if (flatNode == null) |
| return 0; |
| ITextRegion region = flatNode.getRegionAtCharacterOffset(insertPos - 1); |
| if (region == null) |
| return 0; |
| RegionIterator it = new RegionIterator(flatNode, region); |
| boolean found = false; |
| while (it.hasPrev()) { |
| region = it.prev(); |
| // if (region.getType() != CSSRegionContexts.CSS_S && |
| // region.getType() != CSSRegionContexts.CSS_DELIMITER && |
| // region.getType() != |
| // CSSRegionContexts.CSS_DECLARATION_DELIMITER) { |
| if (region.getType() != CSSRegionContexts.CSS_S) { |
| found = true; |
| break; |
| } |
| } |
| int pos = insertPos - (found ? it.getStructuredDocumentRegion().getTextEndOffset(region) : it.getStructuredDocumentRegion().getStartOffset(region)); |
| // flatNode = it.getStructuredDocumentRegion(); |
| // if (found) { |
| // if (region.getLength() != region.getTextLength()) { |
| // pos = insertPos - flatNode.getTextEndOffset(region); |
| // } else { |
| // pos = insertPos - flatNode.getEndOffset(region); |
| // } |
| // } else { |
| // pos = insertPos - flatNode.getStartOffset(region); |
| // } |
| return (pos >= 0) ? pos : 0; |
| } |
| |
| /** |
| * |
| */ |
| String getLineDelimiter(ICSSNode node) { |
| ICSSModel model = (node != null) ? node.getOwnerDocument().getModel() : null; |
| return (model != null) ? model.getStructuredDocument().getLineDelimiter() : "\n"; //$NON-NLS-1$ |
| |
| // TODO : check whether to use model.getLineDelimiter() or |
| // model.getStructuredDocument().getLineDelimiter() |
| } |
| |
| /** |
| * |
| */ |
| protected CompoundRegion[] getOutsideRegions(IStructuredDocument model, IRegion reg) { |
| CompoundRegion[] ret = new CompoundRegion[2]; |
| RegionIterator it = new RegionIterator(model, reg.getOffset()); |
| it.prev(); |
| if (it.hasPrev()) { |
| ITextRegion textRegion = it.prev(); |
| IStructuredDocumentRegion documentRegion = it.getStructuredDocumentRegion(); |
| ret[0] = new CompoundRegion(documentRegion, textRegion); |
| } else { |
| ret[0] = null; |
| } |
| it.reset(model, reg.getOffset() + reg.getLength()); |
| if (it.hasNext()) { |
| ITextRegion textRegion = it.next(); |
| IStructuredDocumentRegion documentRegion = it.getStructuredDocumentRegion(); |
| ret[1] = new CompoundRegion(documentRegion, textRegion); |
| } else { |
| ret[1] = null; |
| } |
| return ret; |
| } |
| |
| /** |
| */ |
| protected CSSSourceGenerator getParentFormatter(ICSSNode node) { |
| ICSSNode parent = node.getParentNode(); |
| if (parent != null) { |
| CSSSourceGenerator formatter = (CSSSourceGenerator) ((INodeNotifier) parent).getAdapterFor(CSSSourceFormatter.class); |
| if (formatter == null) { |
| formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter((INodeNotifier) parent); |
| } |
| return formatter; |
| } |
| return null; |
| } |
| |
| /** |
| * |
| */ |
| protected CompoundRegion[] getRegions(IStructuredDocument model, IRegion reg, IRegion exceptFor, String pickupType) { |
| int start = reg.getOffset(); |
| int end = reg.getOffset() + reg.getLength(); |
| int startE = (exceptFor != null) ? exceptFor.getOffset() : -1; |
| int endE = (exceptFor != null) ? exceptFor.getOffset() + exceptFor.getLength() : 0; |
| |
| ArrayList list = new ArrayList(); |
| IStructuredDocumentRegion flatNode = model.getRegionAtCharacterOffset(start); |
| boolean pickuped = false; |
| while (flatNode != null && flatNode.getStartOffset() < end) { |
| ITextRegionList regionList = flatNode.getRegions(); |
| Iterator it = regionList.iterator(); |
| while (it.hasNext()) { |
| ITextRegion region = (ITextRegion) it.next(); |
| if (flatNode.getStartOffset(region) < start) |
| continue; |
| if (end <= flatNode.getStartOffset(region)) |
| break; |
| if (startE >= 0 && startE <= flatNode.getStartOffset(region) && flatNode.getEndOffset(region) <= endE) |
| continue; |
| if (region.getType() == CSSRegionContexts.CSS_COMMENT || region.getType() == CSSRegionContexts.CSS_CDC || region.getType() == CSSRegionContexts.CSS_CDO) |
| list.add(new CompoundRegion(flatNode, region)); |
| else if (!pickuped && region.getType() == pickupType) { |
| list.add(new CompoundRegion(flatNode, region)); |
| pickuped = true; |
| } |
| } |
| flatNode = flatNode.getNext(); |
| } |
| if (list.size() > 0) { |
| CompoundRegion[] regions = new CompoundRegion[list.size()]; |
| list.toArray(regions); |
| return regions; |
| } |
| return new CompoundRegion[0]; |
| } |
| |
| /** |
| * |
| */ |
| protected CompoundRegion[] getRegionsWithoutWhiteSpaces(IStructuredDocument model, IRegion reg, CSSCleanupStrategy stgy) { |
| int start = reg.getOffset(); |
| int end = reg.getOffset() + reg.getLength() - 1; |
| ArrayList list = new ArrayList(); |
| IStructuredDocumentRegion flatNode = model.getRegionAtCharacterOffset(start); |
| while (flatNode != null && flatNode.getStartOffset() <= end) { |
| ITextRegionList regionList = flatNode.getRegions(); |
| Iterator it = regionList.iterator(); |
| while (it.hasNext()) { |
| ITextRegion region = (ITextRegion) it.next(); |
| if (flatNode.getStartOffset(region) < start) |
| continue; |
| if (end < flatNode.getStartOffset(region)) |
| break; |
| if (region.getType() != CSSRegionContexts.CSS_S || (isCleanup() && !stgy.isFormatSource())) // for |
| // not |
| // formatting |
| // case |
| // on |
| // cleanup |
| // action |
| list.add(new CompoundRegion(flatNode, region)); |
| } |
| flatNode = flatNode.getNext(); |
| } |
| if (list.size() > 0) { |
| CompoundRegion[] regions = new CompoundRegion[list.size()]; |
| list.toArray(regions); |
| return regions; |
| } |
| return new CompoundRegion[0]; |
| } |
| |
| /** |
| * |
| */ |
| public static boolean includes(IRegion region, int start, int end) { |
| if (region == null) |
| return false; |
| |
| return (region.getOffset() <= start) && (end <= region.getOffset() + region.getLength()); |
| } |
| |
| /** |
| * |
| * @return boolean |
| */ |
| protected static boolean isCleanup() { |
| return strategy == CLEANUP; |
| } |
| |
| /** |
| * |
| * @return boolean |
| */ |
| protected static boolean isFormat() { |
| return strategy == FORMAT; |
| } |
| |
| /** |
| * |
| */ |
| protected boolean isIncludesPreEnd(ICSSNode node, IRegion region) { |
| return (node.getFirstChild() != null && ((IndexedRegion) node.getFirstChild()).getStartOffset() == (region.getOffset() + region.getLength())); |
| } |
| |
| /** |
| * |
| */ |
| static protected boolean needS(CompoundRegion region) { |
| return (region != null && region.getType() != CSSRegionContexts.CSS_S); |
| } |
| |
| /** |
| * |
| */ |
| public static IRegion overlappedRegion(IRegion region, int start, int end) { |
| if (overlaps(region, start, end)) { |
| int offset = (region.getOffset() <= start) ? start : region.getOffset(); |
| int length = ((end <= region.getOffset() + region.getLength()) ? end : region.getOffset() + region.getLength()) - offset; |
| return new FormatRegion(offset, length); |
| } |
| return null; |
| } |
| |
| /** |
| * |
| */ |
| public static boolean overlaps(IRegion region, int start, int end) { |
| if (region == null) |
| return false; |
| |
| return (start < region.getOffset() + region.getLength()) && (region.getOffset() < end); |
| } |
| |
| private String getIndentString() { |
| StringBuffer indent = new StringBuffer(); |
| |
| Preferences preferences = CSSCorePlugin.getDefault().getPluginPreferences(); |
| if (preferences != null) { |
| char indentChar = ' '; |
| String indentCharPref = preferences.getString(CSSCorePreferenceNames.INDENTATION_CHAR); |
| if (CSSCorePreferenceNames.TAB.equals(indentCharPref)) { |
| indentChar = '\t'; |
| } |
| int indentationWidth = preferences.getInt(CSSCorePreferenceNames.INDENTATION_SIZE); |
| |
| for (int i = 0; i < indentationWidth; i++) { |
| indent.append(indentChar); |
| } |
| } |
| return indent.toString(); |
| } |
| } |