diff options
Diffstat (limited to 'theme-compiler/src/com/vaadin')
8 files changed, 325 insertions, 162 deletions
diff --git a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java index dbb3e571dc..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; @@ -59,10 +60,12 @@ public class ScssStylesheet extends Node { private static HashMap<Node, Node> lastNodeAdded = new HashMap<Node, Node>(); - private String fileName; + private File file; 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,24 +158,13 @@ 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()); - setFileName(f.getParent()); + setFile(f); return source; } } @@ -171,6 +173,38 @@ public class ScssStylesheet extends Node { } /** + * 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. * * @throws Exception @@ -355,12 +389,28 @@ public class ScssStylesheet extends Node { return mixinDefs.get(name); } - public void setFileName(String fileName) { - this.fileName = fileName; + public void setFile(File file) { + this.file = file; } + /** + * Returns the directory containing this style sheet + * + * @since 7.2 + * @return The directory containing this style sheet + */ + public String getDirectory() { + return file.getParent(); + } + + /** + * Returns the full file name for this style sheet + * + * @since 7.2 + * @return The full file name for this style sheet + */ public String getFileName() { - return fileName; + return file.getPath(); } public static HashMap<Node, Node> getLastNodeAdded() { diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java index 170d8f3e54..170d8f3e54 100755..100644 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java 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..5de1f95264 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/AbstractResolver.java @@ -0,0 +1,200 @@ +/* + * 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.ArrayList; +import java.util.List; +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) { + // Remove a possible ".scss" suffix + identifier = identifier.replaceFirst(".scss$", ""); + + List<String> potentialParentPaths = getPotentialParentPaths( + parentStylesheet, identifier); + + // remove path from identifier as it has already been added to the + // parent path + if (identifier.contains("/")) { + identifier = identifier.substring(identifier.lastIndexOf("/") + 1); + } + + for (String path : potentialParentPaths) { + InputSource source = normalizeAndResolve(path + "/" + identifier); + + if (source != null) { + return source; + } + + // Try to find partial import (_identifier.scss) + source = normalizeAndResolve(path + "/_" + identifier); + + if (source != null) { + return source; + } + + } + + return normalizeAndResolve(identifier); + } + + /** + * Retrieves the parent paths which should be used while resolving relative + * identifiers. By default uses the parent stylesheet location and a + * possible absolute path in the identifier. + * + * @param parentStylesheet + * The parent stylesheet or null if there is no parent + * @param identifier + * The identifier to be resolved + * @return a list of paths in which to look for the relative import + */ + protected List<String> getPotentialParentPaths( + ScssStylesheet parentStylesheet, String identifier) { + List<String> potentialParents = new ArrayList<String>(); + if (parentStylesheet != null) { + potentialParents.add(extractFullPath( + parentStylesheet.getDirectory(), identifier)); + } + + // Identifier can be a full path so extract the path part also as a + // potential parent + if (identifier.contains("/")) { + potentialParents.add(extractFullPath("", identifier)); + } + + return potentialParents; + + } + + /** + * Extracts the full path from the path combined with the identifier + * + * @param path + * The base path + * @param identifier + * The identifier which may contain a path part, separated by "/" + * from the real identifier + * @return a normalized version of the path where identifier does not + * contain any directory information + */ + protected String extractFullPath(String path, String identifier) { + int lastSlashPosition = identifier.lastIndexOf("/"); + if (lastSlashPosition == -1) { + return path; + } + String identifierPath = identifier.substring(0, lastSlashPosition); + if ("".equals(path)) { + return identifierPath; + } else { + return path + "/" + identifierPath; + } + } + + /** + * 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(); + } + +} 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..755073bc4c 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java @@ -15,31 +15,19 @@ */ package com.vaadin.sass.internal.resolver; -import java.io.File; 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) { - // identifier should not have .scss, fileName should - String ext = ".scss"; - if (identifier.endsWith(".css")) { - ext = ".css"; - } + public InputSource resolveNormalized(String identifier) { String fileName = identifier; - if (identifier.endsWith(ext)) { - identifier = identifier.substring(0, - identifier.length() - ext.length()); - } else { - fileName = fileName + ext; + if (!fileName.endsWith(".css")) { + fileName += ".scss"; } - // Ensure only "/" is used, also in Windows - fileName = fileName.replace(File.separatorChar, '/'); - // Filename should be a relative path starting with VAADIN/... int vaadinIdx = fileName.lastIndexOf("VAADIN/"); if (vaadinIdx > -1) { 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..786d0875da 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/FilesystemResolver.java @@ -18,24 +18,46 @@ package com.vaadin.sass.internal.resolver; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; +import java.util.List; import org.w3c.css.sac.InputSource; -public class FilesystemResolver implements ScssStylesheetResolver { +import com.vaadin.sass.internal.ScssStylesheet; +public class FilesystemResolver extends AbstractResolver { + + private String[] customPaths = null; + + public FilesystemResolver(String... customPaths) { + this.customPaths = customPaths; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.sass.internal.resolver.AbstractResolver#getPotentialPaths( + * com.vaadin.sass.internal.ScssStylesheet, java.lang.String) + */ @Override - public InputSource resolve(String identifier) { - // identifier should not have .scss, fileName should - String ext = ".scss"; - if (identifier.endsWith(".css")) { - ext = ".css"; + protected List<String> getPotentialParentPaths( + ScssStylesheet parentStyleSheet, String identifier) { + List<String> potentialPaths = super.getPotentialParentPaths( + parentStyleSheet, identifier); + if (customPaths != null) { + for (String path : customPaths) { + potentialPaths.add(extractFullPath(path, identifier)); + } } + + return potentialPaths; + } + + @Override + public InputSource resolveNormalized(String identifier) { String fileName = identifier; - if (identifier.endsWith(ext)) { - identifier = identifier.substring(0, - identifier.length() - ext.length()); - } else { - fileName = fileName + ext; + if (!fileName.endsWith(".css")) { + fileName += ".scss"; } try { 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<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(); - } - -} 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 cb9896967a..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,25 +56,14 @@ public class ImportNodeHandler { ImportNode importNode = (ImportNode) n; if (!importNode.isPureCssImport()) { try { - StringBuilder filePathBuilder = new StringBuilder( - styleSheet.getFileName()); - 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(importNode.getUri() - + " (parent: " - + ScssStylesheet.get().getFileName() + ")"); + throw new FileNotFoundException("Import '" + + importNode.getUri() + "' in '" + + styleSheet.getFileName() + + "' could not be found"); } traverse(imported); |