From 6d0c6e094fe5ee1253701bd4c897be9e29eea11a Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Mon, 2 Sep 2013 22:37:59 +0300 Subject: Moved import resolving logic to resolvers (#11776) * made resolvers style sheet specific Change-Id: Iab7e755d9e3214896c100dfde10727a1353c4354 --- .../com/vaadin/sass/internal/ScssStylesheet.java | 78 ++++++++---- .../sass/internal/resolver/AbstractResolver.java | 132 +++++++++++++++++++++ .../internal/resolver/ClassloaderResolver.java | 4 +- .../sass/internal/resolver/FilesystemResolver.java | 4 +- .../internal/resolver/ScssStylesheetResolver.java | 7 +- .../sass/internal/resolver/VaadinResolver.java | 90 -------------- .../sass/internal/visitor/ImportNodeHandler.java | 15 +-- 7 files changed, 199 insertions(+), 131 deletions(-) create mode 100644 theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java delete mode 100644 theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java (limited to 'theme-compiler/src') diff --git a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java index 704425e300..7213553e18 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java +++ b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java @@ -19,10 +19,10 @@ package com.vaadin.sass.internal; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.logging.Logger; @@ -35,8 +35,9 @@ import com.vaadin.sass.internal.handler.SCSSErrorHandler; import com.vaadin.sass.internal.parser.ParseException; import com.vaadin.sass.internal.parser.Parser; import com.vaadin.sass.internal.parser.SCSSParseException; +import com.vaadin.sass.internal.resolver.ClassloaderResolver; +import com.vaadin.sass.internal.resolver.FilesystemResolver; import com.vaadin.sass.internal.resolver.ScssStylesheetResolver; -import com.vaadin.sass.internal.resolver.VaadinResolver; import com.vaadin.sass.internal.tree.BlockNode; import com.vaadin.sass.internal.tree.MixinDefNode; import com.vaadin.sass.internal.tree.Node; @@ -63,6 +64,8 @@ public class ScssStylesheet extends Node { private String charset; + private List resolvers = new ArrayList(); + /** * Read in a file SCSS and parse it into a ScssStylesheet * @@ -101,8 +104,8 @@ public class ScssStylesheet extends Node { * @throws CSSException * @throws IOException */ - public static ScssStylesheet get(String identifier, String encoding) - throws CSSException, IOException { + public static ScssStylesheet get(String identifier, + ScssStylesheet parentStylesheet) throws CSSException, IOException { /* * The encoding to be used is passed through "encoding" parameter. the * imported children scss node will have the same encoding as their @@ -122,12 +125,22 @@ public class ScssStylesheet extends Node { SCSSDocumentHandler handler = new SCSSDocumentHandlerImpl(); ScssStylesheet stylesheet = handler.getStyleSheet(); - - InputSource source = stylesheet.resolveStylesheet(identifier); + if (parentStylesheet == null) { + // Use default resolvers + stylesheet.addResolver(new FilesystemResolver()); + stylesheet.addResolver(new ClassloaderResolver()); + } else { + // Use parent resolvers + stylesheet.setResolvers(parentStylesheet.getResolvers()); + } + InputSource source = stylesheet.resolveStylesheet(identifier, + parentStylesheet); if (source == null) { return null; } - source.setEncoding(encoding); + if (parentStylesheet != null) { + source.setEncoding(parentStylesheet.getCharset()); + } Parser parser = new Parser(); parser.setErrorHandler(new SCSSErrorHandler()); @@ -145,21 +158,10 @@ public class ScssStylesheet extends Node { return stylesheet; } - private static ScssStylesheetResolver[] resolvers = null; - - public static void setStylesheetResolvers( - ScssStylesheetResolver... styleSheetResolvers) { - resolvers = Arrays.copyOf(styleSheetResolvers, - styleSheetResolvers.length); - } - - public InputSource resolveStylesheet(String identifier) { - if (resolvers == null) { - setStylesheetResolvers(new VaadinResolver()); - } - - for (ScssStylesheetResolver resolver : resolvers) { - InputSource source = resolver.resolve(identifier); + public InputSource resolveStylesheet(String identifier, + ScssStylesheet parentStylesheet) { + for (ScssStylesheetResolver resolver : getResolvers()) { + InputSource source = resolver.resolve(parentStylesheet, identifier); if (source != null) { File f = new File(source.getURI()); setFile(f); @@ -170,6 +172,38 @@ public class ScssStylesheet extends Node { return null; } + /** + * Retrieves a list of resolvers to use when resolving imports + * + * @since 7.2 + * @return the resolvers used to resolving imports + */ + public List getResolvers() { + return Collections.unmodifiableList(resolvers); + } + + /** + * Sets the list of resolvers to use when resolving imports + * + * @since 7.2 + * @param resolvers + * the resolvers to set + */ + public void setResolvers(List resolvers) { + this.resolvers = new ArrayList(resolvers); + } + + /** + * Adds the given resolver to the resolver list + * + * @since 7.2 + * @param resolver + * The resolver to add + */ + public void addResolver(ScssStylesheetResolver resolver) { + resolvers.add(resolver); + } + /** * Applies all the visitors and compiles SCSS into Css. * diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java new file mode 100644 index 0000000000..ac11f1fa0e --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.vaadin.sass.internal.resolver; + +import java.io.File; +import java.io.Serializable; +import java.util.Stack; + +import org.w3c.css.sac.InputSource; + +import com.vaadin.sass.internal.ScssStylesheet; + +/** + * Base class for resolvers. Implements functionality for locating paths which + * an import can be relative to and helpers for extracting path information from + * the identifier. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public abstract class AbstractResolver implements ScssStylesheetResolver, + Serializable { + /* + * (non-Javadoc) + * + * @see + * com.vaadin.sass.internal.resolver.ScssStylesheetResolver#resolve(java + * .lang.String) + */ + @Override + public InputSource resolve(ScssStylesheet parentStylesheet, + String identifier) { + InputSource source = null; + if (parentStylesheet != null) { + StringBuilder filePathBuilder = new StringBuilder( + parentStylesheet.getFileName()); + filePathBuilder.append(File.separatorChar).append(identifier); + if (!filePathBuilder.toString().endsWith(".scss")) { + filePathBuilder.append(".scss"); + } + source = normalizeAndResolve(filePathBuilder.toString()); + } + + if (source == null) { + source = normalizeAndResolve(identifier); + } + + return source; + } + + /** + * Resolves the normalized version of the given identifier + * + * @param identifier + * The identifier to resolve + * @return An input source if the resolver found one or null otherwise + */ + protected InputSource normalizeAndResolve(String identifier) { + String normalized = normalize(identifier); + return resolveNormalized(normalized); + } + + /** + * Resolves the identifier after it has been normalized using + * {@link #normalize(String)}. + * + * @param identifier + * The normalized identifier + * @return an InputSource if the resolver found a source or null otherwise + */ + protected abstract InputSource resolveNormalized(String identifier); + + /** + * Normalizes "." and ".." from the path string where parent path segments + * can be removed. Preserve leading "..". Also ensure / is used instead of \ + * in all places. + * + * @param path + * A relative or absolute file path + * @return The normalized path + */ + protected String normalize(String path) { + + // Ensure only "/" is used, also in Windows + path = path.replace(File.separatorChar, '/'); + + // Split into segments + String[] segments = path.split("/"); + Stack result = new Stack(); + + // Replace '.' and '..' segments + for (int i = 0; i < segments.length; i++) { + if (segments[i].equals(".")) { + // Segments marked '.' are ignored + + } else if (segments[i].equals("..") && !result.isEmpty() + && !result.lastElement().equals("..")) { + // If segment is ".." then remove the previous iff the previous + // element is not a ".." and the result stack is not empty + result.pop(); + } else { + // Other segments are just added to the stack + result.push(segments[i]); + } + } + + // Reconstruct path + StringBuilder pathBuilder = new StringBuilder(); + for (int i = 0; i < result.size(); i++) { + if (i > 0) { + pathBuilder.append("/"); + } + pathBuilder.append(result.get(i)); + } + return pathBuilder.toString(); + } + +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java index 8711a0a3e9..68752b459a 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java @@ -20,10 +20,10 @@ import java.io.InputStream; import org.w3c.css.sac.InputSource; -public class ClassloaderResolver implements ScssStylesheetResolver { +public class ClassloaderResolver extends AbstractResolver { @Override - public InputSource resolve(String identifier) { + public InputSource resolveNormalized(String identifier) { // identifier should not have .scss, fileName should String ext = ".scss"; if (identifier.endsWith(".css")) { diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java index 9bb1969ab1..3fec33ae13 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java @@ -21,10 +21,10 @@ import java.io.InputStream; import org.w3c.css.sac.InputSource; -public class FilesystemResolver implements ScssStylesheetResolver { +public class FilesystemResolver extends AbstractResolver { @Override - public InputSource resolve(String identifier) { + public InputSource resolveNormalized(String identifier) { // identifier should not have .scss, fileName should String ext = ".scss"; if (identifier.endsWith(".css")) { diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java index 45f10836a3..64b3d10d88 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java @@ -17,6 +17,8 @@ package com.vaadin.sass.internal.resolver; import org.w3c.css.sac.InputSource; +import com.vaadin.sass.internal.ScssStylesheet; + public interface ScssStylesheetResolver { /** * Called with the "identifier" of a stylesheet that the resolver should try @@ -26,9 +28,12 @@ public interface ScssStylesheetResolver { * stylesheet was found, e.g "runo.scss" might result in a URI like * "VAADIN/themes/runo/runo.scss". * + * @param parentStylesheet + * The parent style sheet * @param identifier * used fo find stylesheet * @return InputSource for stylesheet (with URI set) or null if not found */ - public InputSource resolve(String identifier); + public InputSource resolve(ScssStylesheet parentStylesheet, + String identifier); } \ No newline at end of file diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java deleted file mode 100644 index fec16a54c8..0000000000 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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.vaadin.sass.internal.resolver; - -import java.io.File; -import java.util.Stack; - -import org.w3c.css.sac.InputSource; - -public class VaadinResolver implements ScssStylesheetResolver { - - @Override - public InputSource resolve(String identifier) { - - // Remove extra "." and ".." - identifier = normalize(identifier); - - InputSource source = null; - - // Can we find the scss from the file system? - ScssStylesheetResolver resolver = new FilesystemResolver(); - source = resolver.resolve(identifier); - - if (source == null) { - // How about the classpath? - resolver = new ClassloaderResolver(); - source = resolver.resolve(identifier); - } - - return source; - } - - /** - * Normalizes "." and ".." from the path string where parent path segments - * can be removed. Preserve leading "..". - * - * @param path - * A relative or absolute file path - * @return The normalized path - */ - private static String normalize(String path) { - - // Ensure only "/" is used, also in Windows - path = path.replace(File.separatorChar, '/'); - - // Split into segments - String[] segments = path.split("/"); - Stack result = new Stack(); - - // Replace '.' and '..' segments - for (int i = 0; i < segments.length; i++) { - if (segments[i].equals(".")) { - // Segments marked '.' are ignored - - } else if (segments[i].equals("..") && !result.isEmpty() - && !result.lastElement().equals("..")) { - // If segment is ".." then remove the previous iff the previous - // element is not a ".." and the result stack is not empty - result.pop(); - } else { - // Other segments are just added to the stack - result.push(segments[i]); - } - } - - // Reconstruct path - StringBuilder pathBuilder = new StringBuilder(); - for (int i = 0; i < result.size(); i++) { - if (i > 0) { - pathBuilder.append("/"); - } - pathBuilder.append(result.get(i)); - } - return pathBuilder.toString(); - } - -} diff --git a/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java b/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java index 87c7cadcf8..e52767bb5a 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java +++ b/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java @@ -16,7 +16,6 @@ package com.vaadin.sass.internal.visitor; -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; @@ -57,21 +56,9 @@ public class ImportNodeHandler { ImportNode importNode = (ImportNode) n; if (!importNode.isPureCssImport()) { try { - StringBuilder filePathBuilder = new StringBuilder( - styleSheet.getDirectory()); - filePathBuilder.append(File.separatorChar).append( - importNode.getUri()); - if (!filePathBuilder.toString().endsWith(".scss")) { - filePathBuilder.append(".scss"); - } - // set parent's charset to imported node. ScssStylesheet imported = ScssStylesheet.get( - filePathBuilder.toString(), - styleSheet.getCharset()); - if (imported == null) { - imported = ScssStylesheet.get(importNode.getUri()); - } + importNode.getUri(), styleSheet); if (imported == null) { throw new FileNotFoundException("Import '" + importNode.getUri() + "' in '" -- cgit v1.2.3