001    // Copyright 2006, 2008, 2010, 2011 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 java.io.BufferedInputStream;
018    import java.io.IOException;
019    import java.io.InputStream;
020    import java.net.URL;
021    import java.util.Locale;
022    
023    import org.apache.tapestry5.ioc.Resource;
024    import org.apache.tapestry5.ioc.util.LocalizedNameGenerator;
025    
026    /**
027     * Abstract implementation of {@link Resource}. Subclasses must implement the abstract methods {@link Resource#toURL()}
028     * and {@link #newResource(String)} as well as toString(), hashCode() and equals().
029     */
030    public abstract class AbstractResource implements Resource
031    {
032        private final String path;
033    
034        protected AbstractResource(String path)
035        {
036            assert path != null;
037            this.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            assert relativePath != null;
062            StringBuilder builder = new StringBuilder(getFolder());
063    
064            for (String term : relativePath.split("/"))
065            {
066                // This will occur if the relative path contains sequential slashes
067    
068                if (term.equals(""))
069                    continue;
070    
071                if (term.equals("."))
072                    continue;
073    
074                if (term.equals(".."))
075                {
076                    int slashx = builder.lastIndexOf("/");
077    
078                    // TODO: slashx < 0 (i.e., no slash)
079    
080                    // Trim path to content before the slash
081    
082                    builder.setLength(slashx);
083                    continue;
084                }
085    
086                // TODO: term blank or otherwise invalid?
087                // TODO: final term should not be "." or "..", or for that matter, the
088                // name of a folder, since a Resource should be a file within
089                // a folder.
090    
091                if (builder.length() > 0)
092                    builder.append("/");
093    
094                builder.append(term);
095            }
096    
097            return createResource(builder.toString());
098        }
099    
100        public final Resource forLocale(Locale locale)
101        {
102            for (String path : new LocalizedNameGenerator(this.path, locale))
103            {
104                Resource potential = createResource(path);
105    
106                if (potential.exists())
107                    return potential;
108            }
109    
110            return null;
111        }
112    
113        public final Resource withExtension(String extension)
114        {
115            assert InternalUtils.isNonBlank(extension);
116            int dotx = path.lastIndexOf('.');
117    
118            if (dotx < 0)
119                return createResource(path + "." + extension);
120    
121            return createResource(path.substring(0, dotx + 1) + extension);
122        }
123    
124        /**
125         * Creates a new resource, unless the path matches the current Resource's path (in which case, this resource is
126         * returned).
127         */
128        private Resource createResource(String path)
129        {
130            if (this.path.equals(path))
131                return this;
132    
133            return newResource(path);
134        }
135    
136        /**
137         * Simple check for whether {@link #toURL()} returns null or not.
138         */
139        public boolean exists()
140        {
141            return toURL() != null;
142        }
143    
144        /**
145         * Obtains the URL for the Resource and opens the stream, wrapped by a BufferedInputStream.
146         */
147        public InputStream openStream() throws IOException
148        {
149            URL url = toURL();
150    
151            if (url == null)
152                return null;
153    
154            return new BufferedInputStream(url.openStream());
155        }
156    
157        /**
158         * Factory method provided by subclasses.
159         */
160        protected abstract Resource newResource(String path);
161    }