001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.util.resource;
018
019import java.io.ByteArrayInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.InputStreamReader;
023import java.io.Reader;
024import java.io.UnsupportedEncodingException;
025import java.nio.charset.Charset;
026import java.time.Instant;
027import org.apache.wicket.util.io.IOUtils;
028import org.apache.wicket.util.io.Streams;
029import org.apache.wicket.util.lang.Bytes;
030import org.apache.wicket.util.string.Strings;
031
032
033/**
034 * Base class for string resources.
035 * 
036 * @author Jonathan Locke
037 */
038public abstract class AbstractStringResourceStream extends AbstractResourceStream
039        implements
040                IStringResourceStream
041{
042        private static final long serialVersionUID = 1L;
043
044        /** The content-type applied in case the resource stream's default constructor is used */
045        public static final String DEFAULT_CONTENT_TYPE = "text";
046
047        /** Charset name for resource */
048        private String charsetName;
049
050        /** MIME content type */
051        private final String contentType;
052
053        /** The last time this stylesheet was modified */
054        private Instant lastModified = null;
055
056        /**
057         * Constructor.
058         */
059        public AbstractStringResourceStream()
060        {
061                this(DEFAULT_CONTENT_TYPE);
062        }
063
064        /**
065         * Constructor.
066         * 
067         * @param contentType
068         *            The mime type of this resource, such as "image/jpeg" or "text/html"
069         */
070        public AbstractStringResourceStream(final String contentType)
071        {
072                // TODO null for contentType is allowed? or should the default be applied instead?
073                this.contentType = contentType;
074
075                lastModified = Instant.now();
076        }
077
078        /**
079         * @return This resource as a String.
080         */
081        @Override
082        public String asString()
083        {
084                Reader reader = null;
085                try
086                {
087                        if (charsetName == null)
088                        {
089                                reader = new InputStreamReader(getInputStream());
090                        }
091                        else
092                        {
093                                reader = new InputStreamReader(getInputStream(), getCharset());
094                        }
095                        return Streams.readString(reader);
096                }
097                catch (IOException e)
098                {
099                        throw new RuntimeException("Unable to read resource as String", e);
100                }
101                catch (ResourceStreamNotFoundException e)
102                {
103                        throw new RuntimeException("Unable to read resource as String", e);
104                }
105                finally
106                {
107                        IOUtils.closeQuietly(reader);
108                        IOUtils.closeQuietly(this);
109                }
110        }
111
112        /**
113         * @return Charset for resource
114         */
115        protected Charset getCharset()
116        {
117                // java.nio.Charset is not serializable so we can only store the name
118                return (charsetName != null) ? Charset.forName(charsetName) : null;
119        }
120
121        /**
122         * Sets the character set used for reading this resource.
123         * 
124         * @param charset
125         *            Charset for component
126         */
127        @Override
128        public void setCharset(final Charset charset)
129        {
130                // java.nio.Charset itself is not serializable so we can only store the name
131                charsetName = (charset != null) ? charset.name() : null;
132        }
133
134        /**
135         * @see org.apache.wicket.util.resource.IResourceStream#close()
136         */
137        @Override
138        public void close() throws IOException
139        {
140        }
141
142        /**
143         * @see org.apache.wicket.util.resource.IResourceStream#getContentType()
144         */
145        @Override
146        public String getContentType()
147        {
148                return contentType;
149        }
150
151        /**
152         * @see org.apache.wicket.util.resource.IResourceStream#getInputStream()
153         */
154        @Override
155        public InputStream getInputStream() throws ResourceStreamNotFoundException
156        {
157                final byte[] bytes;
158                if (getCharset() != null)
159                {
160                        try
161                        {
162                                bytes = getString().getBytes(getCharset().name());
163                        }
164                        catch (UnsupportedEncodingException e)
165                        {
166                                throw new ResourceStreamNotFoundException("Could not encode resource", e);
167                        }
168                }
169                else
170                {
171                        bytes = getString().getBytes();
172                }
173                return new ByteArrayInputStream(bytes);
174        }
175
176        /**
177         * @see org.apache.wicket.util.watch.IModifiable#lastModifiedTime()
178         */
179        @Override
180        public Instant lastModifiedTime()
181        {
182                return lastModified;
183        }
184
185        /**
186         * @param lastModified
187         *            The lastModified to set.
188         */
189        public void setLastModified(final Instant lastModified)
190        {
191                this.lastModified = lastModified;
192        }
193
194        /**
195         * @return The string resource
196         */
197        protected abstract String getString();
198
199        @Override
200        public final Bytes length()
201        {
202                int lengthInBytes = Strings.lengthInBytes(getString(), getCharset());
203                return Bytes.bytes(lengthInBytes);
204        }
205}