]> source.dussan.org Git - vaadin-framework.git/commitdiff
Moved import resolving logic to resolvers (#11776)
authorArtur Signell <artur@vaadin.com>
Mon, 2 Sep 2013 19:37:59 +0000 (22:37 +0300)
committerVaadin Code Review <review@vaadin.com>
Thu, 26 Sep 2013 06:10:13 +0000 (06:10 +0000)
* made resolvers style sheet specific

Change-Id: Iab7e755d9e3214896c100dfde10727a1353c4354

theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java [new file with mode: 0644]
theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java
theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java
theme-compiler/src/com/vaadin/sass/internal/resolver/ScssStylesheetResolver.java
theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java [deleted file]
theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java
theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java

index 704425e30088627376a4c79963d452c8263b1e6f..7213553e18be72031b35fdb8a8a74cec3624bb77 100644 (file)
@@ -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<ScssStylesheetResolver> resolvers = new ArrayList<ScssStylesheetResolver>();
+
     /**
      * 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<ScssStylesheetResolver> 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<ScssStylesheetResolver> resolvers) {
+        this.resolvers = new ArrayList<ScssStylesheetResolver>(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 (file)
index 0000000..ac11f1f
--- /dev/null
@@ -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<String> result = new Stack<String>();
+
+        // 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();
+    }
+
+}
index 8711a0a3e965af30f051dcafb6a9f582f89a8d05..68752b459afdd4331489fdc77daea8ba3b5d0c18 100644 (file)
@@ -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")) {
index 9bb1969ab19243115dc15a85cdcf5078d00d8052..3fec33ae13f682171180947f216234dadbdcd9ab 100644 (file)
@@ -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")) {
index 45f10836a3c7944a3b43a9ccde1165fea402da9c..64b3d10d881aae04af7f1f5cd2b63514238d55b7 100644 (file)
@@ -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 (file)
index fec16a5..0000000
+++ /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<String> result = new Stack<String>();
-
-        // 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();
-    }
-
-}
index 87c7cadcf80eb54417b37f412953a95c090e19bf..e52767bb5a3d5848436f241640063f93a769adcf 100644 (file)
@@ -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 '"
index 59b49888c27fee7f7b447788673d73182a0c8787..018314274784819b2e7462fa266bba9af40f11f0 100644 (file)
@@ -40,16 +40,26 @@ import java.lang.reflect.Method;
 import org.junit.Assert;
 import org.junit.Test;
 
-import com.vaadin.sass.internal.resolver.VaadinResolver;
+import com.vaadin.sass.internal.resolver.AbstractResolver;
+import com.vaadin.sass.internal.resolver.ClassloaderResolver;
+import com.vaadin.sass.internal.resolver.FilesystemResolver;
 
 public class VaadinResolverTest {
 
     @Test
-    public void testPathNormalization() throws Exception {
+    public void testFilesystemResolverPathNormalization() throws Exception {
+        testPathNormalization(new FilesystemResolver());
+    }
+
+    @Test
+    public void testClassloaderResolverPathNormalization() throws Exception {
+        testPathNormalization(new ClassloaderResolver());
+    }
 
-        VaadinResolver resolver = new VaadinResolver();
+    public void testPathNormalization(AbstractResolver resolver)
+            throws Exception {
 
-        Method normalizeMethod = VaadinResolver.class.getDeclaredMethod(
+        Method normalizeMethod = AbstractResolver.class.getDeclaredMethod(
                 "normalize", String.class);
         normalizeMethod.setAccessible(true);