001    // Copyright 2006, 2008 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry5.ioc.internal.util;
016    
017    import org.apache.tapestry5.ioc.Resource;
018    import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
019    import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
020    
021    import java.io.BufferedInputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.net.URL;
025    import java.util.Locale;
026    
027    /**
028     * Abstract implementation of {@link Resource}. Subclasses must implement the abstract methods {@link Resource#toURL()}
029     * and {@link #newResource(String)} as well as toString(), hashCode() and equals().
030     */
031    public abstract class AbstractResource implements Resource
032    {
033        private final String path;
034    
035        protected AbstractResource(String path)
036        {
037            this.path = notNull(path, "path");
038        }
039    
040        public final String getPath()
041        {
042            return path;
043        }
044    
045        public final String getFile()
046        {
047            int slashx = path.lastIndexOf('/');
048    
049            return path.substring(slashx + 1);
050        }
051    
052        public final String getFolder()
053        {
054            int slashx = path.lastIndexOf('/');
055    
056            return (slashx < 0) ? "" : path.substring(0, slashx);
057        }
058    
059        public final Resource forFile(String relativePath)
060        {
061            Defense.notNull(relativePath, "relativePath");
062    
063            StringBuilder builder = new StringBuilder(getFolder());
064    
065            for (String term : relativePath.split("/"))
066            {
067                // This will occur if the relative path contains sequential slashes
068    
069                if (term.equals("")) continue;
070    
071                if (term.equals(".")) continue;
072    
073                if (term.equals(".."))
074                {
075                    int slashx = builder.lastIndexOf("/");
076    
077                    // TODO: slashx < 0 (i.e., no slash)
078    
079                    // Trim path to content before the slash
080    
081                    builder.setLength(slashx);
082                    continue;
083                }
084    
085                // TODO: term blank or otherwise invalid?
086                // TODO: final term should not be "." or "..", or for that matter, the
087                // name of a folder, since a Resource should be a file within
088                // a folder.
089    
090                if (builder.length() > 0) builder.append("/");
091    
092                builder.append(term);
093            }
094    
095            return createResource(builder.toString());
096        }
097    
098        public final Resource forLocale(Locale locale)
099        {
100            for (String path : new LocalizedNameGenerator(this.path, locale))
101            {
102                Resource potential = createResource(path);
103    
104                if (potential.exists()) return potential;
105            }
106    
107            return null;
108        }
109    
110        public final Resource withExtension(String extension)
111        {
112            notBlank(extension, "extension");
113    
114            int dotx = path.lastIndexOf('.');
115    
116            if (dotx < 0) return createResource(path + "." + extension);
117    
118            return createResource(path.substring(0, dotx + 1) + extension);
119        }
120    
121        /**
122         * Creates a new resource, unless the path matches the current Resource's path (in which case, this resource is
123         * returned).
124         */
125        private Resource createResource(String path)
126        {
127            if (this.path.equals(path)) return this;
128    
129            return newResource(path);
130        }
131    
132        /**
133         * Simple check for whether {@link #toURL()} returns null or not.
134         */
135        public boolean exists()
136        {
137            return toURL() != null;
138        }
139    
140        /**
141         * Obtains the URL for the Resource and opens the stream, wrapped by a BufferedInputStream.
142         */
143        public InputStream openStream() throws IOException
144        {
145            URL url = toURL();
146    
147            if (url == null) return null;
148    
149            return new BufferedInputStream(url.openStream());
150        }
151    
152        /**
153         * Factory method provided by subclasses.
154         */
155        protected abstract Resource newResource(String path);
156    }