e4c2d81742e8abdba77730aa692e5ee2197e3a5f
[sonitus.git] / src / main / java / net / pterodactylus / sonitus / data / filter / PipelineFilter.java
1 /*
2  * Sonitus - PipelineFilter.java - Copyright © 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sonitus.data.filter;
19
20 import java.io.IOException;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25
26 import net.pterodactylus.sonitus.data.AbstractFilter;
27 import net.pterodactylus.sonitus.data.DataPacket;
28 import net.pterodactylus.sonitus.data.Filter;
29 import net.pterodactylus.sonitus.data.Metadata;
30 import net.pterodactylus.sonitus.data.Pipeline.Connection;
31
32 import com.google.common.collect.Lists;
33 import com.google.common.collect.Maps;
34
35 /**
36  * {@link Filter} that combines several filters into one.
37  *
38  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
39  */
40 public class PipelineFilter extends AbstractFilter implements Filter {
41
42         /** The first filter. */
43         private final Filter source;
44
45         /** All following filters. */
46         private final List<Filter> filters = Lists.newArrayList();
47
48         /** The last filter (for convenience). */
49         private final Filter lastFilter;
50
51         /** The connections for each filter. */
52         private final Map<Filter, Connection> filterConnections = Maps.newHashMap();
53
54         /**
55          * Creates a new pipeline filter.
56          *
57          * @param name
58          *              The name of the filter
59          * @param source
60          *              The first source of the filter
61          * @param filters
62          *              All other filters in correct order
63          */
64         private PipelineFilter(String name, Filter source, Collection<Filter> filters) {
65                 super(name);
66                 this.source = source;
67                 this.filters.addAll(filters);
68                 this.lastFilter = this.filters.get(filters.size() - 1);
69         }
70
71         //
72         // FILTER METHODS
73         //
74
75         @Override
76         public Metadata metadata() {
77                 return lastFilter.metadata();
78         }
79
80         @Override
81         public void open(Metadata metadata) throws IOException {
82                 /* open the source and all filters in the correct order. */
83                 source.open(metadata);
84                 Metadata currentMetadata = source.metadata();
85                 Filter currentSource = source;
86                 for (Filter filter : filters) {
87                         filter.open(currentMetadata);
88                         currentMetadata = filter.metadata();
89                         Connection connection = new Connection(currentSource, Arrays.asList(filter));
90                         filterConnections.put(filter, connection);
91                         String threadName = String.format("%s → %s", connection.source().name(), filter.name());
92                         new Thread(connection, threadName).start();
93                         currentSource = filter;
94                 }
95                 metadataUpdated(currentMetadata);
96         }
97
98         @Override
99         public DataPacket get(int bufferSize) throws IOException {
100                 if (filterConnections.get(lastFilter).ioException().isPresent()) {
101                         throw filterConnections.get(lastFilter).ioException().get();
102                 }
103                 return lastFilter.get(bufferSize);
104         }
105
106         @Override
107         public void process(DataPacket dataPacket) throws IOException {
108                 source.process(dataPacket);
109         }
110
111         //
112         // STATIC METHODS
113         //
114
115         /**
116          * Returns a builder that can create pipeline filters.
117          *
118          * @param source
119          *              The source filter of the pipeline
120          * @return The pipeline filter builder
121          */
122         public static Builder builder(Filter source) {
123                 return new Builder(source);
124         }
125
126         /**
127          * Builder for a {@link PipelineFilter}.
128          *
129          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
130          */
131         public static class Builder {
132
133                 /** The source of the pipeline. */
134                 private final Filter source;
135
136                 /** All other filters of the pipeline. */
137                 private final List<Filter> filters = Lists.newArrayList();
138
139                 /**
140                  * Creates a new builder with the given source.
141                  *
142                  * @param source
143                  *              The source of the pipeline filter
144                  */
145                 private Builder(Filter source) {
146                         this.source = source;
147                 }
148
149                 /**
150                  * Connects the given filter at the end of the pipeline being build.
151                  *
152                  * @param filter
153                  *              The filter to add
154                  * @return This builder
155                  */
156                 public Builder to(Filter filter) {
157                         filters.add(filter);
158                         return this;
159                 }
160
161                 /**
162                  * Builds a filter using the given name. If no filters other than the source
163                  * have been added, only the source filter is being returned.
164                  *
165                  * @param name
166                  *              The name of the pipeline filter to build
167                  * @return The created filter, or the source filter
168                  */
169                 public Filter build(String name) {
170                         if (filters.isEmpty()) {
171                                 return source;
172                         }
173                         return new PipelineFilter(name, source, filters);
174                 }
175
176         }
177
178 }