| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| MosaicEngine |
|
| 1.78125;1.781 | ||||
| MosaicEngine$ConfigItem |
|
| 1.78125;1.781 |
| 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 | 0 | protected static boolean DEBUG = false; |
| 40 | 0 | 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 | 0 | protected int tileHeight = 50; |
| 50 | 0 | protected Date lastMosaicUpdate = new Date(0); |
| 51 | protected Set mosaicSet; // The Set to fetch our pics from | |
| 52 | ||
| 53 | 0 | protected JobLogEntry jobLogEntry = null; |
| 54 | ||
| 55 | /** A hash of all of the configured parameters */ | |
| 56 | 0 | 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 | 0 | protected LinkedList<String> configList = new LinkedList<String>(); |
| 59 | ||
| 60 | 0 | 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 | 0 | public MosaicEngine() { |
| 66 | 0 | addConfig("rateWeight", "rate multiplier when selecting pics", ConfigItem.TYPE_DOUBLE, 1.0); |
| 67 | ||
| 68 | 0 | } |
| 69 | /** Set the reference to the PicMan API. | |
| 70 | * @param pmf The PicManFacade | |
| 71 | */ | |
| 72 | public void setPicMan(PicManFacade pmf) { | |
| 73 | 0 | this.pmf = pmf; |
| 74 | 0 | this.dbf = pmf.getDB(); |
| 75 | 0 | } |
| 76 | ||
| 77 | /** Enable logger output on this module | |
| 78 | * @param debugFlag true == turn on debugging. | |
| 79 | */ | |
| 80 | public void setDEBUG(boolean debugFlag) { | |
| 81 | 0 | DEBUG = debugFlag; |
| 82 | 0 | } |
| 83 | ||
| 84 | /** | |
| 85 | * @return the lastMosaicUpdate | |
| 86 | */ | |
| 87 | public Date getLastMosaicUpdate() { | |
| 88 | 0 | return lastMosaicUpdate; |
| 89 | } | |
| 90 | ||
| 91 | protected void setLastMosaicUpdate() { | |
| 92 | 0 | this.lastMosaicUpdate = new Date(); |
| 93 | 0 | } |
| 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 | 0 | return tileHeight; |
| 101 | } | |
| 102 | ||
| 103 | /** | |
| 104 | * @param tileHeight the tileHeight to set | |
| 105 | */ | |
| 106 | public void setTileHeight(int tileHeight) { | |
| 107 | 0 | this.tileHeight = tileHeight; |
| 108 | 0 | } |
| 109 | ||
| 110 | /** Fetch the image that we built / are building. | |
| 111 | * @return the bufferedImage | |
| 112 | */ | |
| 113 | public BufferedImage getImage() { | |
| 114 | 0 | 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 | 0 | if (this.mosaic != null) |
| 122 | 0 | return(this.mosaic.getName()); |
| 123 | 0 | 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 | 0 | if (this.mosaic != null) |
| 131 | 0 | return(this.mosaic.getMasterPic()); |
| 132 | 0 | 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 | 0 | if (this.mosaic != null) |
| 139 | 0 | return(this.mosaic.getOutPic()); |
| 140 | 0 | 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 | 0 | if (jobLogEntry == null) |
| 148 | 0 | return("xx:xx"); |
| 149 | 0 | 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 | 0 | this.mosaic = mosaic; |
| 157 | 0 | this.mosaicSet = pmf.getDB().getSet(mosaic.getSid()); |
| 158 | 0 | this.masterPic = pmf.getDB().getPic(mosaic.getMasterPic()); |
| 159 | 0 | this.mid = mosaic.getMid(); |
| 160 | 0 | this.tileHeight = mosaic.getTileHeight(); |
| 161 | 0 | this.setupBuildParameters(); |
| 162 | 0 | this.bi = pmf.getPicReader().readPic(this.masterPic); |
| 163 | 0 | setLastMosaicUpdate(); |
| 164 | 0 | } |
| 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 | 0 | Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); |
| 172 | 0 | ImageWriter writer = (ImageWriter)writers.next(); |
| 173 | 0 | File file = new File(this.mosaic.getOutPic()); |
| 174 | 0 | if (DEBUG) |
| 175 | 0 | logger.info("Writing jpg to '" + file.getPath() + "'"); |
| 176 | try { | |
| 177 | 0 | ImageOutputStream oos = ImageIO.createImageOutputStream(file); |
| 178 | 0 | writer.setOutput(oos); |
| 179 | 0 | writer.write(bi); |
| 180 | 0 | oos.close(); |
| 181 | 0 | } catch (Exception e) { |
| 182 | // bi = null; | |
| 183 | 0 | pmf.addError(new Exception ("Can't write jpeg to '"+ file.getPath() + "'",e)); |
| 184 | 0 | return; |
| 185 | 0 | } |
| 186 | 0 | if (makeBackup) |
| 187 | 0 | CopyFile.makeBackup(pmf, file); |
| 188 | 0 | } |
| 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 | 0 | jobLogEntry = new JobLogEntry(); |
| 196 | 0 | jobLogEntry.setType(JobLogEntry.MOSAIC); |
| 197 | 0 | jobLogEntry.setName(this.mosaic.getName()); |
| 198 | 0 | pmf.addJobToLog(jobLogEntry); |
| 199 | 0 | this.mosaic.setStartTime(new Date()); |
| 200 | 0 | boolean ret = false; |
| 201 | try { | |
| 202 | 0 | ret = _build(); |
| 203 | 0 | } catch (Exception e) { |
| 204 | 0 | jobLogEntry.setEndTime(); |
| 205 | 0 | throw e; |
| 206 | 0 | } |
| 207 | 0 | jobLogEntry.setEndTime(); |
| 208 | 0 | if (!mosaic.isBatch()) |
| 209 | 0 | pmf.getDB().storeMosaic(mosaic); |
| 210 | 0 | outputMosaicFile(mosaic.isMakeBackup()); |
| 211 | 0 | return(ret); |
| 212 | } | |
| 213 | ||
| 214 | protected boolean _build() throws Exception { | |
| 215 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | public ConfigItem(String name, String description, int type, int order) { |
| 262 | 0 | this.name = name; |
| 263 | 0 | this.description = description; |
| 264 | 0 | this.type = type; |
| 265 | 0 | this.order = order; |
| 266 | 0 | } |
| 267 | ||
| 268 | /** | |
| 269 | * @return the valueInt | |
| 270 | */ | |
| 271 | public int getValueInt() { | |
| 272 | 0 | return valueInt; |
| 273 | } | |
| 274 | ||
| 275 | /** | |
| 276 | * @param valueInt the valueInt to set | |
| 277 | */ | |
| 278 | public void setValueInt(int valueInt) { | |
| 279 | 0 | this.valueInt = valueInt; |
| 280 | 0 | } |
| 281 | ||
| 282 | /** | |
| 283 | * @return the valueDouble | |
| 284 | */ | |
| 285 | public double getValueDouble() { | |
| 286 | 0 | return valueDouble; |
| 287 | } | |
| 288 | ||
| 289 | /** | |
| 290 | * @param valueDouble the valueDouble to set | |
| 291 | */ | |
| 292 | public void setValueDouble(double valueDouble) { | |
| 293 | 0 | this.valueDouble = valueDouble; |
| 294 | 0 | } |
| 295 | ||
| 296 | /** | |
| 297 | * @return the valueString | |
| 298 | */ | |
| 299 | public String getValueString() { | |
| 300 | 0 | return valueString; |
| 301 | } | |
| 302 | ||
| 303 | /** | |
| 304 | * @param valueString the valueString to set | |
| 305 | */ | |
| 306 | public void setValueString(String valueString) { | |
| 307 | 0 | this.valueString = valueString; |
| 308 | 0 | } |
| 309 | ||
| 310 | /** Get the name of this ConfigItem. | |
| 311 | * @return the name | |
| 312 | */ | |
| 313 | public String getName() { | |
| 314 | 0 | return name; |
| 315 | } | |
| 316 | ||
| 317 | /** | |
| 318 | * @return the description | |
| 319 | */ | |
| 320 | public String getDescription() { | |
| 321 | 0 | return description; |
| 322 | } | |
| 323 | ||
| 324 | /** | |
| 325 | * @return the type | |
| 326 | */ | |
| 327 | public int getType() { | |
| 328 | 0 | return type; |
| 329 | } | |
| 330 | ||
| 331 | } | |
| 332 | ||
| 333 | protected void addConfig(String name, String description, int type, int value) { | |
| 334 | 0 | ConfigItem ci = new ConfigItem(name, description, type, configMap.size()); |
| 335 | 0 | ci.valueInt = value; |
| 336 | 0 | configMap.put(name, ci); |
| 337 | 0 | configList.add(name); |
| 338 | 0 | } |
| 339 | protected void addConfig(String name, String description, int type, double value) { | |
| 340 | 0 | ConfigItem ci = new ConfigItem(name, description, type, configMap.size()); |
| 341 | 0 | ci.valueDouble = value; |
| 342 | 0 | configMap.put(name, ci); |
| 343 | 0 | configList.add(name); |
| 344 | 0 | } |
| 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 | 0 | return(configMap); |
| 351 | } | |
| 352 | ||
| 353 | private void setupBuildParameters() { | |
| 354 | 0 | HashMap<String, String> engineConfig = mosaic.getEngineConfig(); |
| 355 | 0 | for (String key : configMap.keySet()) { |
| 356 | 0 | ConfigItem ci = configMap.get(key); |
| 357 | 0 | String v = engineConfig.get(key); |
| 358 | 0 | if (v != null) { |
| 359 | 0 | switch (ci.type) { |
| 360 | case ConfigItem.TYPE_INT: | |
| 361 | 0 | int i = -1; |
| 362 | try { | |
| 363 | 0 | i = Integer.parseInt(v); |
| 364 | 0 | } catch (NumberFormatException e) { |
| 365 | 0 | pmf.addError(e); |
| 366 | 0 | logger.info(e); |
| 367 | 0 | i = -1; |
| 368 | 0 | } |
| 369 | 0 | buildParameters.put(key, new Integer(i)); |
| 370 | 0 | break; |
| 371 | case ConfigItem.TYPE_DOUBLE: | |
| 372 | 0 | double d = 0.0; |
| 373 | try { | |
| 374 | 0 | if (!v.isEmpty()) |
| 375 | 0 | d = Double.parseDouble(v); |
| 376 | 0 | } catch (NumberFormatException e) { |
| 377 | 0 | pmf.addError(e); |
| 378 | 0 | logger.info(e); |
| 379 | 0 | d = 0.0; |
| 380 | 0 | } |
| 381 | 0 | buildParameters.put(key, new Double(d)); |
| 382 | 0 | break; |
| 383 | default: | |
| 384 | 0 | Exception e = new Exception("Unhandled type " + ci.type); |
| 385 | 0 | pmf.addError(e); |
| 386 | 0 | logger.info(e); |
| 387 | 0 | break; |
| 388 | } | |
| 389 | } else { | |
| 390 | 0 | switch (ci.type) { |
| 391 | case ConfigItem.TYPE_INT: | |
| 392 | 0 | buildParameters.put(key, new Integer(ci.getValueInt())); |
| 393 | 0 | break; |
| 394 | case ConfigItem.TYPE_DOUBLE: | |
| 395 | 0 | buildParameters.put(key, new Double(ci.getValueDouble())); |
| 396 | 0 | break; |
| 397 | default: | |
| 398 | 0 | Exception e = new Exception("Unhandled default type " + ci.type); |
| 399 | 0 | pmf.addError(e); |
| 400 | 0 | logger.info(e); |
| 401 | break; | |
| 402 | } | |
| 403 | } | |
| 404 | 0 | } |
| 405 | 0 | } |
| 406 | } |