View Javadoc
1   /******************************************************************************
2    * MosaicEngine.java - Base class for the Mosaic engines
3    * 
4    * PicMan - The BuckoSoft Picture Manager in Java
5    * Copyright(c) 2007 - Dick Balaska
6    * 
7    */
8   package com.buckosoft.PicMan.business.mosaic;
9   
10  import java.awt.image.BufferedImage;
11  import java.io.File;
12  import java.util.Date;
13  import java.util.HashMap;
14  import java.util.Iterator;
15  import java.util.LinkedList;
16  
17  import javax.imageio.ImageIO;
18  import javax.imageio.ImageWriter;
19  import javax.imageio.stream.ImageOutputStream;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import com.buckosoft.PicMan.business.PicManFacade;
25  import com.buckosoft.PicMan.business.util.CopyFile;
26  import com.buckosoft.PicMan.db.DatabaseFacade;
27  import com.buckosoft.PicMan.domain.JobLogEntry;
28  import com.buckosoft.PicMan.domain.Mosaic;
29  import com.buckosoft.PicMan.domain.Pic;
30  import com.buckosoft.PicMan.domain.Set;
31  
32  /** Base class for the mosaic engines
33   * @author Dick Balaska
34   * @since 2007/12/23
35   * @see <a href="http://cvs.buckosoft.com/Projects/PicMan/PicMan/src/main/java/com/buckosoft/PicMan/business/mosaic/MosaicEngine.java">MosaicEngine.java</a>
36   * @see com.buckosoft.PicMan.business.mosaic.engine
37   */
38  public class MosaicEngine {
39  	protected static boolean DEBUG = false;
40  	private final Log logger = LogFactory.getLog(getClass());
41  
42  	protected	PicManFacade	pmf;
43  	protected	DatabaseFacade	dbf;
44  
45  	protected	Mosaic			mosaic;
46  	protected	Pic				masterPic;
47  	protected	int				mid;
48  	protected	BufferedImage	bi;
49  	protected	int				tileHeight = 50;
50  	protected	Date			lastMosaicUpdate = new Date(0);
51  	protected	Set				mosaicSet;		// The Set to fetch our pics from
52  
53  	protected	JobLogEntry		jobLogEntry = null;
54  
55  	/** A hash of all of the configured parameters */
56  	protected HashMap<String, ConfigItem>	configMap = new HashMap<String, ConfigItem>();
57  	/** The names of the configured parameters, so we can pull them in a reasonable order for display. */
58  	protected LinkedList<String>			configList = new LinkedList<String>();
59  
60  	protected HashMap<String, Object>	buildParameters = new HashMap<String, Object>();
61  
62  	/** Default constructor
63  	 * Base class constructor.  Place your engine config attributes in your constructor. 
64  	 */
65  	public MosaicEngine() {
66  		addConfig("rateWeight", "rate multiplier when selecting pics", ConfigItem.TYPE_DOUBLE, 1.0);
67  		
68  	}
69  	/** Set the reference to the PicMan API.
70  	 * @param pmf The PicManFacade
71  	 */
72  	public	void setPicMan(PicManFacade pmf) {
73  		this.pmf = pmf;
74  		this.dbf = pmf.getDB();
75  	}
76  
77  	/** Enable logger output on this module
78  	 * @param debugFlag true == turn on debugging.
79  	 */
80  	public void setDEBUG(boolean debugFlag) {
81  		DEBUG = debugFlag;
82  	}
83  
84  	/**
85  	 * @return the lastMosaicUpdate
86  	 */
87  	public Date getLastMosaicUpdate() {
88  		return lastMosaicUpdate;
89  	}
90  
91  	protected void setLastMosaicUpdate() {
92  		this.lastMosaicUpdate = new Date();
93  	}
94  	
95  	/** Get the Tile Height / thumbnail height to be used to build this Mosaic.
96  	 * This assumes an engine deals in tileHeights, which is true for now.
97  	 * @return the tileHeight
98  	 */
99  	public int getTileHeight() {
100 		return tileHeight;
101 	}
102 
103 	/**
104 	 * @param tileHeight the tileHeight to set
105 	 */
106 	public void setTileHeight(int tileHeight) {
107 		this.tileHeight = tileHeight;
108 	}
109 
110 	/** Fetch the image that we built / are building.
111 	 * @return the bufferedImage
112 	 */
113 	public BufferedImage getImage() {
114 		return bi;
115 	}
116 	
117 	/** Get the name of the Mosaic that we are building.
118 	 * @return The Mosaic name from the Mosaic.
119 	 */
120 	public String	getName() {
121 		if (this.mosaic != null)
122 			return(this.mosaic.getName());
123 		return("No Name");
124 	}
125 
126 	/** Get the name of the master pic that we are building from.
127 	 * @return The name of the master pic.
128 	 */
129 	public String	getMasterPicName() {
130 		if (this.mosaic != null)
131 			return(this.mosaic.getMasterPic());
132 		return("");
133 	}
134 	/** Get the name of the output file that will be written.
135 	 * @return	The User specified output file. i.e. "C:\myMosaics\Mosaic1.jpg"
136 	 */
137 	public String	getOutputFileName() {
138 		if (this.mosaic != null)
139 			return(this.mosaic.getOutPic());
140 		return("No Filename");
141 	}
142 
143 	/** Fetch how long this Engine has been running.
144 	 * @return Something like "1:23:45"
145 	 */
146 	public String	getDurationAsString() {
147 		if (jobLogEntry == null)
148 			return("xx:xx");
149 		return(jobLogEntry.getDurationAsString());
150 	}
151 
152 	/** Set the Mosaic that we are going to build.
153 	 * @param mosaic The Mosaic parameters as specified by the User.
154 	 */
155 	public	void	setMosaic(Mosaic mosaic) {
156 		this.mosaic = mosaic;
157 		this.mosaicSet = pmf.getDB().getSet(mosaic.getSid());
158 		this.masterPic = pmf.getDB().getPic(mosaic.getMasterPic());
159 		this.mid = mosaic.getMid();
160 		this.tileHeight = mosaic.getTileHeight();
161 		this.setupBuildParameters();
162 		this.bi = pmf.getPicReader().readPic(this.masterPic);
163 		setLastMosaicUpdate();
164 	}
165 
166 
167 	/** Write the built image to the jpg specified in outPic
168 	 * @param makeBackup Maintain a set of backups of the output mosaic.
169 	 */
170 	public	void	outputMosaicFile(boolean makeBackup) {
171 		Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
172 		ImageWriter writer = (ImageWriter)writers.next();
173 		File file = new File(this.mosaic.getOutPic());
174 		if (DEBUG)
175 			logger.info("Writing jpg to '" + file.getPath() + "'");
176 		try {
177 			ImageOutputStream oos = ImageIO.createImageOutputStream(file);
178 			writer.setOutput(oos);
179 			writer.write(bi);
180 			oos.close();
181 		} catch (Exception e) {
182 //			bi = null;
183 			pmf.addError(new Exception ("Can't write jpeg to '"+ file.getPath() + "'",e));
184 			return;
185 		}
186 		if (makeBackup)
187 			CopyFile.makeBackup(pmf, file);
188 	}
189 
190 	/** Build the Mosaic.
191 	 * @return true if there is more work to do.
192 	 * @throws Exception Something went wrong.
193 	 */
194 	public boolean build() throws Exception {
195 		jobLogEntry = new JobLogEntry();
196 		jobLogEntry.setType(JobLogEntry.MOSAIC);
197 		jobLogEntry.setName(this.mosaic.getName());
198 		pmf.addJobToLog(jobLogEntry);
199 		this.mosaic.setStartTime(new Date());
200 		boolean ret = false;
201 		try {
202 			ret = _build();
203 		} catch (Exception e) {
204 			jobLogEntry.setEndTime();
205 			throw e;
206 		}
207 		jobLogEntry.setEndTime();
208 		if (!mosaic.isBatch())
209 			pmf.getDB().storeMosaic(mosaic);
210 		outputMosaicFile(mosaic.isMakeBackup());
211 		return(ret);
212 	}
213 
214 	protected boolean _build() throws Exception {
215 		throw new Exception("Should be overridden");
216 		
217 	}
218 
219 	/** Get the engine's build status.  This should be overridden by the actual engine.
220 	 * @return i.e. SecondRibbon returns "Calc: pass=13 p=901"
221 	 */
222 	public String getStatus() {
223 		return("Should be overridden");
224 	}
225 	
226 	/** Get the engine's semi-static build info.  This should be overridden by the actual engine.
227 	 * @return i.e. SecondRibbon returns "tileRows=154 maxCol=2048 pics=5820"
228 	 */
229 	public String getInfo() {
230 		return("Should be overridden");
231 	}
232 	
233 	/** Support for runtime MosaicEngine configuration.
234 	 * These define the available configurable attributes for an engine.
235 	 * @author Dick Balaska
236 	 * @since 2009/07/17
237 	 * @version $Revision: 1.5 $ <br> $Date: 2014/06/21 04:35:32 $
238 	 * @see MosaicEngine
239 	 */
240 	public class ConfigItem {
241 		public final static int TYPE_STRING 	= 1;
242 		public final static int TYPE_INT 		= 2;
243 		public final static int TYPE_DOUBLE		= 3;
244 		public final static int TYPE_BOOLEAN	= 4;
245 
246 		private String	name;
247 		private String	description;
248 		@SuppressWarnings("unused")
249 		private int		order;			// aborted attempt to display the config fields in order
250 		private int		type;
251 		private int		valueInt;
252 		private double	valueDouble;
253 		private String	valueString;
254 		
255 		/** Construct a ConfigItem with these parameters
256 		 * @param name The name of the item
257 		 * @param description A description to go in the web page
258 		 * @param type String/int/bool/etc.
259 		 * @param order The order that this item was entered, for display
260 		 */
261 		public ConfigItem(String name, String description, int type, int order) {
262 			this.name = name;
263 			this.description = description;
264 			this.type = type;
265 			this.order = order;
266 		}
267 
268 		/**
269 		 * @return the valueInt
270 		 */
271 		public int getValueInt() {
272 			return valueInt;
273 		}
274 
275 		/**
276 		 * @param valueInt the valueInt to set
277 		 */
278 		public void setValueInt(int valueInt) {
279 			this.valueInt = valueInt;
280 		}
281 
282 		/**
283 		 * @return the valueDouble
284 		 */
285 		public double getValueDouble() {
286 			return valueDouble;
287 		}
288 
289 		/**
290 		 * @param valueDouble the valueDouble to set
291 		 */
292 		public void setValueDouble(double valueDouble) {
293 			this.valueDouble = valueDouble;
294 		}
295 
296 		/**
297 		 * @return the valueString
298 		 */
299 		public String getValueString() {
300 			return valueString;
301 		}
302 
303 		/**
304 		 * @param valueString the valueString to set
305 		 */
306 		public void setValueString(String valueString) {
307 			this.valueString = valueString;
308 		}
309 
310 		/** Get the name of this ConfigItem.
311 		 * @return the name
312 		 */
313 		public String getName() {
314 			return name;
315 		}
316 
317 		/**
318 		 * @return the description
319 		 */
320 		public String getDescription() {
321 			return description;
322 		}
323 
324 		/**
325 		 * @return the type
326 		 */
327 		public int getType() {
328 			return type;
329 		}
330 		
331 	}
332 
333 	protected void addConfig(String name, String description, int type, int value) {
334 		ConfigItem ci = new ConfigItem(name, description, type, configMap.size());
335 		ci.valueInt = value;
336 		configMap.put(name, ci);
337 		configList.add(name);
338 	}
339 	protected void addConfig(String name, String description, int type, double value) {
340 		ConfigItem ci = new ConfigItem(name, description, type, configMap.size());
341 		ci.valueDouble = value;
342 		configMap.put(name, ci);
343 		configList.add(name);
344 	}
345 
346 	/** A map of the available ConfigItems for an engine.
347 	 * @return The map of ConfigItems for this engine keyed by the item name.
348 	 */
349 	public HashMap<String, ConfigItem> getConfigMap() {
350 		return(configMap);
351 	}
352 	
353 	private void setupBuildParameters() {
354 		HashMap<String, String> engineConfig = mosaic.getEngineConfig();
355 		for (String key : configMap.keySet()) {
356 			ConfigItem ci = configMap.get(key);
357 			String v = engineConfig.get(key);
358 			if (v != null) {
359 				switch (ci.type) {
360 				case ConfigItem.TYPE_INT:
361 					int i = -1;
362 					try {
363 						i = Integer.parseInt(v);
364 					} catch (NumberFormatException e) {
365 						pmf.addError(e);
366 						logger.info(e);
367 						i = -1;
368 					}
369 					buildParameters.put(key, new Integer(i));
370 					break;
371 				case ConfigItem.TYPE_DOUBLE:
372 					double d = 0.0;
373 					try {
374 						if (!v.isEmpty())
375 							d = Double.parseDouble(v);
376 					} catch (NumberFormatException e) {
377 						pmf.addError(e);
378 						logger.info(e);
379 						d = 0.0;
380 					}
381 					buildParameters.put(key, new Double(d));
382 					break;
383 				default:
384 					Exception e = new Exception("Unhandled type " + ci.type);
385 					pmf.addError(e);
386 					logger.info(e);
387 					break;
388 				}
389 			} else {
390 				switch (ci.type) {
391 				case ConfigItem.TYPE_INT:
392 					buildParameters.put(key, new Integer(ci.getValueInt()));
393 					break;
394 				case ConfigItem.TYPE_DOUBLE:
395 					buildParameters.put(key, new Double(ci.getValueDouble()));
396 					break;
397 				default:
398 					Exception e = new Exception("Unhandled default type " + ci.type);
399 					pmf.addError(e);
400 					logger.info(e);
401 					break;
402 				}
403 			}
404 		}
405 	}
406 }