View Javadoc

1   /*
2    * Copyright 2013 smartics, Kronseder & Reiner GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package de.smartics.properties.config.transfer.templatestream;
17  
18  import java.io.PrintWriter;
19  
20  import org.apache.commons.lang.StringUtils;
21  
22  import de.smartics.properties.api.config.domain.Property;
23  import de.smartics.properties.api.config.domain.PropertyProvider;
24  import de.smartics.properties.api.config.domain.key.ConfigurationKey;
25  import de.smartics.properties.api.config.transfer.PropertySink;
26  import de.smartics.properties.api.config.transfer.TransferException;
27  import de.smartics.util.lang.Arg;
28  
29  /**
30   * Writes properties to a folder.
31   */
32  public final class StreamPropertySink implements PropertySink
33  { // NOPMD
34    // ********************************* Fields *********************************
35  
36    // --- constants ------------------------------------------------------------
37  
38    // --- members --------------------------------------------------------------
39  
40    /**
41     * The wrapper for the whole document to be written to the stream.
42     */
43    private final Wrapper documentWrapper;
44  
45    /**
46     * The wrapper for a single line.
47     */
48    private final Wrapper lineWrapper;
49  
50    /**
51     * The template to apply on each property.
52     */
53    private final String template;
54  
55    /**
56     * The stream to write to. The stream is closed by calling {@link #close()} by
57     * the client.
58     */
59    private final PrintWriter stream;
60  
61    /**
62     * The escaper for config key, property name and value.
63     */
64    private final ValueEscaper escaper;
65  
66    // ****************************** Initializer *******************************
67  
68    // ****************************** Constructors ******************************
69  
70    /**
71     * Convenience constructor using a default table descriptor with no wrappers.
72     * <p>
73     * The stream is closed by calling {@link #close()} by the client.
74     * </p>
75     *
76     * @param template the template to apply on each property.
77     * @param stream the stream to write to.
78     * @param escaper the escaper for config key, property name and value.
79     * @throws NullPointerException if {@code template} or {@code stream} is
80     *           <code>null</code>.
81     * @throws IllegalArgumentException if {@code template} is blank.
82     */
83    public StreamPropertySink(final String template, final PrintWriter stream,
84        final ValueEscaper escaper) throws NullPointerException,
85      IllegalArgumentException
86    {
87      this(null, template, stream, escaper);
88    }
89  
90    /**
91     * Convenience constructor with no wrappers.
92     * <p>
93     * The stream is closed by calling {@link #close()} by the client.
94     * </p>
95     *
96     * @param descriptor the descriptor for table and column names to store
97     *          property values.
98     * @param template the template to apply on each property.
99     * @param stream the stream to write to.
100    * @param escaper the escaper for config key, property name and value.
101    * @throws NullPointerException if {@code template} or {@code stream} is
102    *           <code>null</code>.
103    * @throws IllegalArgumentException if {@code template} is blank.
104    */
105   public StreamPropertySink(final TableDescriptor descriptor,
106       final String template, final PrintWriter stream,
107       final ValueEscaper escaper) throws NullPointerException,
108     IllegalArgumentException
109   {
110     this(descriptor, null, null, template, stream, escaper);
111   }
112 
113   /**
114    * Default constructor.
115    * <p>
116    * The stream is closed by calling {@link #close()} by the client.
117    * </p>
118    *
119    * @param descriptor the descriptor for table and column names to store
120    *          property values.
121    * @param documentWrapper the introduction to write to the stream at the
122    *          start.
123    * @param lineWrapper the text to write to the stream at the end before
124    *          closing the stream.
125    * @param template the template to apply on each property.
126    * @param stream the stream to write to.
127    * @param escaper the escaper for config key, property name and value.
128    * @throws NullPointerException if {@code template} , {@code stream} , or
129    *           {@escaper} is <code>null</code>.
130    * @throws IllegalArgumentException if {@code template} is blank.
131    */
132   public StreamPropertySink(final TableDescriptor descriptor,
133       final Wrapper documentWrapper, final Wrapper lineWrapper,
134       final String template, final PrintWriter stream,
135       final ValueEscaper escaper) throws NullPointerException,
136     IllegalArgumentException
137   {
138     final TableDescriptor desc = createDescriptor(descriptor);
139     this.documentWrapper = createWrapper(documentWrapper);
140     this.lineWrapper = createWrapper(lineWrapper);
141     this.template =
142         applyStandardAndDescriptor(desc,
143             Arg.checkNotBlank("template", template));
144     this.stream = Arg.checkNotNull("stream", stream);
145     this.escaper = Arg.checkNotNull("escaper", escaper);
146   }
147 
148   // ****************************** Inner Classes *****************************
149 
150   // ********************************* Methods ********************************
151 
152   // --- init -----------------------------------------------------------------
153 
154   private static Wrapper createWrapper(final Wrapper wrapper)
155   {
156     final Wrapper norm = wrapper == null ? new Wrapper() : wrapper;
157 
158     norm.setIntro(applyStandard(norm.getIntro()));
159     norm.setExtro(applyStandard(norm.getExtro()));
160 
161     return norm;
162   }
163 
164   private static TableDescriptor createDescriptor(
165       final TableDescriptor descriptor)
166   {
167     if (descriptor != null)
168     {
169       return descriptor;
170     }
171     return new TableDescriptor.Builder().build();
172   }
173 
174   private static String applyStandard(final String template)
175   {
176     final String applied =
177         StringUtils.replaceEach(template, new String[] { "${newline}" },
178             new String[] { "\n" });
179     return applied;
180   }
181 
182   private static String applyStandardAndDescriptor(
183       final TableDescriptor descriptor, final String template)
184   {
185     final String applied =
186         StringUtils.replaceEach(
187             template,
188             new String[] { "${newline}", "${table}", "${configColumn}",
189                           "${nameColumn}", "${valueColumn}" },
190             new String[] { "\n", descriptor.getTable(),
191                           descriptor.getConfigColumn(),
192                           descriptor.getNameColumn(),
193                           descriptor.getValueColumn() });
194     return applied;
195   }
196 
197   // --- get&set --------------------------------------------------------------
198 
199   // --- business -------------------------------------------------------------
200 
201   /**
202    * {@inheritDoc}
203    * <p>
204    * This implementation is a no-op since a stream cannot be cleared.
205    * </p>
206    */
207   @Override
208   public void clear() throws TransferException
209   {
210   }
211 
212   @Override
213   public void write(final PropertyProvider provider) throws TransferException
214   {
215     writeDocumentIntro();
216     writeOne(provider);
217     writeDocumentExtro();
218   }
219 
220   private void writeDocumentIntro()
221   {
222     final String intro = documentWrapper.getIntro();
223     if (intro != null)
224     {
225       stream.write(intro);
226     }
227   }
228 
229   private void writeDocumentExtro()
230   {
231     final String extro = documentWrapper.getExtro();
232     if (extro != null)
233     {
234       stream.write(extro);
235     }
236   }
237 
238   private void writeOne(final PropertyProvider provider)
239   {
240     final String intro = lineWrapper.getIntro();
241     if (intro != null)
242     {
243       final String location = String.valueOf(provider.getSourceId());
244       final String line =
245           StringUtils.replaceEach(intro, new String[] { "${location}" },
246               new String[] { location });
247       stream.write(line);
248     }
249 
250     final ConfigurationKey<?> configKey = provider.getConfigurationKey();
251     final String key = escaper.escape(configKey.toString());
252 
253     for (final Property property : provider.getProperties())
254     {
255       final String line = applyTemplate(key, property);
256       stream.write(line);
257     }
258 
259     final String extro = lineWrapper.getExtro();
260     if (extro != null)
261     {
262       stream.write(extro);
263     }
264   }
265 
266   private String applyTemplate(final String escapedKey, final Property property)
267   {
268     final String name = escaper.escape(property.getName());
269     final String value = escaper.escape(property.getValue());
270     final String line =
271         StringUtils.replaceEach(template,
272             new String[] { "${configKey}", "${name}", "${value}" },
273             new String[] { escapedKey, name, value });
274     return line;
275   }
276 
277   @Override
278   public void write(final Iterable<PropertyProvider> providers)
279     throws TransferException
280   {
281     writeDocumentIntro();
282 
283     for (final PropertyProvider provider : providers)
284     {
285       writeOne(provider);
286     }
287 
288     writeDocumentExtro();
289   }
290 
291   /**
292    * {@inheritDoc}
293    * <p>
294    * This is a no-op.
295    * </p>
296    */
297   @Override
298   public void close() throws TransferException
299   {
300     stream.close();
301   }
302 
303   // --- object basics --------------------------------------------------------
304 
305 }