/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.jooby.internal;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
import org.jooby.Err;
import org.jooby.MediaType;
import org.jooby.MediaType.Matcher;
import org.jooby.Renderer;
import org.jooby.Status;
import org.jooby.View;
import com.google.common.base.Joiner;
public abstract class AbstractRendererContext implements Renderer.Context {
private Locale locale;
private List<Renderer> renderers;
private Matcher matcher;
protected final Charset charset;
private Map<String, Object> locals;
private List<MediaType> produces;
private boolean committed;
private int rsize;
public AbstractRendererContext(final List<Renderer> renderers,
final List<MediaType> produces, final Charset charset, final Locale locale,
final Map<String, Object> locals) {
this.renderers = renderers;
this.produces = produces;
this.charset = charset;
this.locale = locale;
this.locals = locals;
rsize = this.renderers.size();
}
public void render(final Object value) throws Exception {
int i = 0;
List<String> notFound = new ArrayList<>();
while (!committed && i < rsize) {
Renderer next = renderers.get(i);
try {
next.render(value, this);
} catch (FileNotFoundException ex) {
// view engine should recover from a template not found
if (next instanceof View.Engine) {
notFound.add(next.toString());
} else {
throw ex;
}
}
i += 1;
}
if (!committed) {
if (notFound.size() > 0) {
throw new FileNotFoundException("Template not found: " + ((View) value).name() + " in "
+ notFound);
}
throw new Err(Status.NOT_ACCEPTABLE, Joiner.on(", ").join(produces));
}
}
@Override
public Locale locale() {
return locale;
}
@Override
public Map<String, Object> locals() {
return locals;
}
@Override
public boolean accepts(final MediaType type) {
if (matcher == null) {
matcher = MediaType.matcher(produces);
}
return matcher.matches(type);
}
@Override
public Renderer.Context type(final MediaType type) {
// NOOP
return this;
}
@Override
public Renderer.Context length(final long length) {
// NOOP
return this;
}
@Override
public Charset charset() {
return charset;
}
@Override
public void send(final CharBuffer buffer) throws Exception {
type(MediaType.html);
send(charset.encode(buffer));
}
@Override
public void send(final Reader reader) throws Exception {
type(MediaType.html);
send(new ReaderInputStream(reader, charset));
}
@Override
public void send(final String text) throws Exception {
type(MediaType.html);
byte[] bytes = text.getBytes(charset);
length(bytes.length);
_send(bytes);
committed = true;
}
@Override
public void send(final byte[] bytes) throws Exception {
type(MediaType.octetstream);
length(bytes.length);
_send(bytes);
committed = true;
}
@Override
public void send(final ByteBuffer buffer) throws Exception {
type(MediaType.octetstream);
length(buffer.remaining());
_send(buffer);
committed = true;
}
@Override
public void send(final FileChannel file) throws Exception {
type(MediaType.octetstream);
length(file.size());
_send(file);
committed = true;
}
@Override
public void send(final InputStream stream) throws Exception {
type(MediaType.octetstream);
if (stream instanceof FileInputStream) {
send(((FileInputStream) stream).getChannel());
} else {
_send(stream);
}
committed = true;
}
protected void setCommitted() {
committed = true;
}
@Override
public String toString() {
return renderers.stream().map(Renderer::name).collect(Collectors.joining(", "));
}
protected abstract void _send(final byte[] bytes) throws Exception;
protected abstract void _send(final ByteBuffer buffer) throws Exception;
protected abstract void _send(final FileChannel file) throws Exception;
protected abstract void _send(final InputStream stream) throws Exception;
}