Package org.wicketstuff.mergedresources.resources

Source Code of org.wicketstuff.mergedresources.resources.MergedResourceStream$LocalizedMergedResourceStream

/**
* Copyright 2010 Molindo GmbH
*
* 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 org.wicketstuff.mergedresources.resources;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import org.apache.wicket.Application;
import org.apache.wicket.IClusterable;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.util.listener.IChangeListener;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.resource.locator.ResourceNameIterator;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.time.Time;
import org.apache.wicket.util.watch.IModificationWatcher;
import org.wicketstuff.mergedresources.ResourceSpec;
import org.wicketstuff.mergedresources.preprocess.IResourcePreProcessor;

import at.molindo.utils.io.StreamUtils;

public class MergedResourceStream implements IResourceStream {
  private static final long serialVersionUID = 1L;
  private static transient final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MergedResourceStream.class);

  private final ResourceSpec[] _specs;
  private Locale _locale;
  private final String _style;
  private LocalizedMergedResourceStream _localizedMergedResourceStream;
  private final IResourcePreProcessor _preProcessor;

  /**
   * @deprecated use ResourceSpec[] instead of scopes[] and files[]
   */
  @Deprecated
  public MergedResourceStream(final Class<?>[] scopes, final String[] files, final Locale locale, final String style) {
    this(ResourceSpec.toResourceSpecs(scopes, files), locale, style, null);
  }

  public MergedResourceStream(final ResourceSpec[] specs, final Locale locale, final String style,
      IResourcePreProcessor preProcessor) {
    _specs = specs.clone();
    _locale = locale;
    _style = style;
    _preProcessor = preProcessor;
  }

  @Override
  public void close() throws IOException {
    // do nothing
  }

  @Override
  public String getContentType() {
    return getLocalizedMergedResourceStream().getContentType();
  }

  @Override
  public InputStream getInputStream() throws ResourceStreamNotFoundException {
    return getLocalizedMergedResourceStream().getInputStream();
  }

  @Override
  public Locale getLocale() {
    return _locale;
  }

  @Override
  public long length() {
    return getLocalizedMergedResourceStream().getContent().length;
  }

  @Override
  public void setLocale(final Locale locale) {
    _locale = locale;
  }

  @Override
  public Time lastModifiedTime() {
    return getLocalizedMergedResourceStream().getLastModifiedTime();
  }

  private LocalizedMergedResourceStream getLocalizedMergedResourceStream() {
    synchronized (this) {
      if (_localizedMergedResourceStream == null) {
        _localizedMergedResourceStream = new LocalizedMergedResourceStream();
      }
      return _localizedMergedResourceStream;
    }
  }

  private final class LocalizedMergedResourceStream implements IClusterable {
    private static final long serialVersionUID = 1L;
    private final byte[] _content;
    private final String _contentType;
    private final Time _lastModifiedTime;

    private LocalizedMergedResourceStream() {
      Time max = null;
      // final StringWriter w = new StringWriter(4096);
      ByteArrayOutputStream out = new ByteArrayOutputStream(4096);

      final ArrayList<IResourceStream> resourceStreams = new ArrayList<IResourceStream>(_specs.length);

      String contentType = null;
      for (int i = 0; i < _specs.length; i++) {
        final Class<?> scope = _specs[i].getScope();
        final String fileName = _specs[i].getFile();

        final IResourceStream resourceStream = findResourceStream(scope, fileName);
        if (contentType != null) {
          if (resourceStream.getContentType() != null
              && !contentType.equalsIgnoreCase(resourceStream.getContentType())) {
            log.warn("content types of merged resources don't match: '" + resourceStream.getContentType()
                + "' and '" + contentType + "'");
          }
        } else {
          contentType = resourceStream.getContentType();
        }

        try {

          final Time lastModified = resourceStream.lastModifiedTime();
          if (max == null || lastModified != null && lastModified.after(max)) {
            max = lastModified;
          }
          if (i > 0) {
            writeFileSeparator(out);
          }
          // process content from single spec
          byte[] preprocessed = preProcess(_specs[i], StreamUtils.bytes(resourceStream.getInputStream()));
          writeContent(out, new ByteArrayInputStream(preprocessed));
          resourceStreams.add(resourceStream);
        } catch (final IOException e) {
          throw new WicketRuntimeException("failed to read from " + resourceStream, e);
        } catch (final ResourceStreamNotFoundException e) {
          throw new WicketRuntimeException("did not find resource", e);
        } finally {
          try {
            if (resourceStream != null) {
              resourceStream.close();
            }
          } catch (final IOException e) {
            log.warn("error while closing reader", e);
          }
        }

      }
      _contentType = contentType;

      _content = toContent(preProcess(null, out.toByteArray()));
      _lastModifiedTime = max == null ? Time.now() : max;
      watchForChanges(resourceStreams);
    }

    private IResourceStream findResourceStream(final Class<?> scope, final String fileName) {
      // Create the base path
      final String path = Strings.beforeLast(scope.getName(), '.').replace('.', '/') + '/'
          + Strings.beforeLast(fileName, '.');
      // Iterator over all the combinations
      final ResourceNameIterator iter = new ResourceNameIterator(path, _style, _locale, Strings.afterLast(
          fileName, '.'));

      IResourceStream resourceStream = null;
      while (resourceStream == null && iter.hasNext()) {
        final String resourceName = iter.next();
        resourceStream = Application.get().getResourceSettings().getResourceStreamLocator()
            .locate(scope, resourceName, _style, _locale, null);
      }

      if (resourceStream == null) {
        throw new WicketRuntimeException("did not find IResourceStream for "
            + Arrays.asList(scope, fileName, _style, _locale));
      }

      return resourceStream;
    }

    private void writeContent(final OutputStream out, final InputStream resourceStream) throws IOException {
      StreamUtils.copy(resourceStream, out);
      out.flush();
    }

    private void writeFileSeparator(ByteArrayOutputStream out) throws IOException {
      out.write(getFileSeparator());
    }

    private byte[] getFileSeparator() {
      return isPlainText() ? "\n\n".getBytes() : new byte[0];
    }

    private void watchForChanges(final List<IResourceStream> resourceStreams) {
      // Watch file in the future
      final IModificationWatcher watcher = Application.get().getResourceSettings().getResourceWatcher(true);
      if (watcher != null) {
        final IChangeListener listener = new IChangeListener() {
          @Override
          public void onChange() {
            log.info("merged resource has changed");
            synchronized (MergedResourceStream.this) {
              for (final IResourceStream resourceStream : resourceStreams) {
                watcher.remove(resourceStream);
              }
              _localizedMergedResourceStream = null;
            }
          }
        };
        for (final IResourceStream resourceStream : resourceStreams) {
          watcher.add(resourceStream, listener);
        }
      }
    }

    public InputStream getInputStream() {
      return new ByteArrayInputStream(getContent());
    }

    public byte[] getContent() {
      return _content;
    }

    public Time getLastModifiedTime() {
      return _lastModifiedTime;
    }

    public String getContentType() {
      return _contentType;
    }
  }

  protected byte[] toContent(final byte[] content) {
    return content;
  }

  public boolean isPlainText() {
    // TODO maybe something smarter?
    return true;
  }

  public byte[] preProcess(ResourceSpec resourceSpec, byte[] content) {
    return _preProcessor != null ? _preProcessor.preProcess(resourceSpec, content) : content;
  }
}
TOP

Related Classes of org.wicketstuff.mergedresources.resources.MergedResourceStream$LocalizedMergedResourceStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.