aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/rendering/Renderer.java
blob: a61c657b70943333a9a80dc2f70564a01ff002ea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.rendering;

import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.yahoo.io.ByteWriter;
import com.yahoo.processing.Request;
import com.yahoo.processing.execution.Execution;

import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

/**
 * Renders a search result to a writer synchronously - the result is completely rendered when the render method returns..
 * The renderers are cloned just before rendering,
 * and must therefore obey the following contract:
 *
 * <ol>
 * <li>At construction time, only final members shall be initialized, and these
 * must refer to immutable data only.</li>
 * <li>State mutated during rendering shall be initialized in the init method.</li>
 * </ol>
 *
 * @author Tony Vaagenes
 */
abstract public class Renderer extends com.yahoo.processing.rendering.Renderer<Result> {

    /**
     * Renders synchronously and returns when rendering is complete.
     *
     * @return a future which is always completed to true
     */
    @Override
    public final ListenableFuture<Boolean> render(OutputStream stream, Result response, Execution execution, Request request) {
        Writer writer = null;
        try {
            writer = createWriter(stream,response);
            render(writer, response);
        }
        catch (IOException e) {
            throw new RuntimeException(e);
        }
        finally {
            if (writer !=null)
                try { writer.close(); } catch (IOException e2) {};
        }
        SettableFuture<Boolean> completed=SettableFuture.create();
        completed.set(true);
        return completed;
    }

    /**
     * Renders the result to the writer.
     */
    protected abstract void render(Writer writer, Result result) throws IOException;

    private Writer createWriter(OutputStream stream,Result result) {
        Charset cs = Charset.forName(getCharacterEncoding(result));
        CharsetEncoder encoder = cs.newEncoder();
        return new ByteWriter(stream, encoder);
    }

    public String getCharacterEncoding(Result result) {
        String encoding = result.getQuery().getModel().getEncoding();
        return (encoding != null) ? encoding : getEncoding();
    }

    /**
     * @return The summary class to fill the hits with if no summary class was
     * specified in the query presentation.
     */
    public String getDefaultSummaryClass() {
        return null;
    }

    /** Returns the encoding of the query, or the encoding given by the template if none is set */
    public final String getRequestedEncoding(Query query) {
        String encoding = query.getModel().getEncoding();
        if (encoding != null) return encoding;
        return getEncoding();
    }

    /**
     * Used to create a separate instance for each result to render.
     */
    @Override
    public Renderer clone() {
        return (Renderer) super.clone();
    }

}