/******************************************************************************* * Copyright 2011 Google Inc. All Rights Reserved. * * 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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.gwtplugins.gwt.eclipse.gss; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.TypedPosition; import org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatter; import org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatterFactory; import org.eclipse.wst.css.core.internal.provisional.document.ICSSDocument; import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; import com.google.gdt.eclipse.core.StringUtilities; import com.google.gwt.eclipse.core.GWTPluginLog; import com.google.gwt.eclipse.core.formatter.AbstractFormattingStrategy; import com.gwtplugins.gwt.eclipse.gss.model.GssResourceAwareModelLoader; /** * The base formatting strategy for CSS that extracts the CSS from the document * (whose model may not be a CSS model), formats the extracted CSS, validates * the formatting is correct, and re-applies it to the document. * <p> * Validation is required because there are cases (e.g. "@media { b { } }") * where the built-in CSS formatter corrupts the CSS. In order to prevent * changes (and excess undos) to the document if the formatted CSS is corrupt, * we use the extracted CSS as a sandbox for the formatter. */ @SuppressWarnings("restriction") public class ExtractingGssFormattingStrategy extends AbstractFormattingStrategy { /** * Allows subclasses to process the formatted CSS block further. * <p> * The transformation should not touch anything but whitespace. * * @param formattedCssBlock the formatted CSS block as a string * @return the processed CSS block, or null to prevent formatting */ protected String adjustFormattedCssWhitespace(String formattedCssBlock, IDocument originalDocument, TypedPosition partition, GssExtractor extractor) { return formattedCssBlock; } @Override protected void format(IDocument document, TypedPosition partition) { GssExtractor extractor = GssExtractor.extract(document, partition.getOffset(), partition.getLength(), new GssResourceAwareModelLoader()); if (extractor == null) { GWTPluginLog.logError("Could not format CSS block due to error in extracting the document."); return; } ICSSDocument cssDocument = extractor.getCssDocument(); String formattedCssBlock = formatCss(cssDocument); formattedCssBlock = adjustFormattedCssWhitespace(formattedCssBlock, document, partition, extractor); if (formattedCssBlock == null) { return; } try { String currentText = document.get(partition.getOffset(), partition.getLength()); if (formattedCssBlock.equals(currentText)) { // Do nothing return; } if (!StringUtilities.equalsIgnoreWhitespace(formattedCssBlock, currentText, true)) { // Do nothing, since the formatting cause non-whitespace/non-case // changes, which a formatter is not supposed to do // (Give it a dummy exception so it prints a stack trace) GWTPluginLog.logError( new Exception(), "Could not format text because the CSS formatter caused non-whitespace/non-case changes. Please ensure your CSS is valid."); return; } document.replace(partition.getOffset(), partition.getLength(), formattedCssBlock.toString()); } catch (BadLocationException e) { GWTPluginLog.logWarning(e, "Could not format CSS block."); } } private String formatCss(ICSSDocument cssDocument) { CSSSourceFormatter formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter( (INodeNotifier) cssDocument); String formattedCssBlock = formatter.format(cssDocument).toString(); return formattedCssBlock; } }