Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/flexstor/flexdbserver/services/asset/iptc/FileInfoPSDService.java


1   /*
2    * FileInfoPSDService.java
3    *
4    * Copyright $Date: 2003/08/11 02:22:34 $ FLEXSTOR.net Inc.
5    *
6    * This work is licensed for use and distribution under license terms found at
7    * http://www.flexstor.org/license.html
8    *
9    */
10  
11  package com.flexstor.flexdbserver.services.asset.iptc;
12  
13  import java.util.Enumeration;
14  import java.util.Hashtable;
15  import java.util.Vector;
16  
17  import com.flexstor.common.data.ActionData;
18  import com.flexstor.common.data.ActionResult;
19  import com.flexstor.common.data.ejb.disguiserecord.AssetRoleData;
20  import com.flexstor.common.data.ejb.disguiserecord.DisguiseAssetRecordData;
21  import com.flexstor.common.data.ejb.disguiserecord.DisguiseElementRecordData;
22  import com.flexstor.common.data.ejb.disguiserecord.DisguiseFieldRecordData;
23  import com.flexstor.common.data.ejb.disguiserecord.DisguiseRecordData;
24  import com.flexstor.common.errorlogger.FlexError;
25  import com.flexstor.common.importprocessor.ImportData;
26  import com.flexstor.common.importprocessor.ImportResult;
27  import com.flexstor.common.io.xfile.FlexXFile;
28  import com.flexstor.common.services.ServiceArgumentsI;
29  import com.flexstor.common.util.Diagnostic;
30  import com.flexstor.ejb.bucket.persist.ServerBucketExtendData;
31  import com.flexstor.ejb.disguise.persist.ServerDisguiseExtendData;
32  import com.flexstor.ejb.field.persist.ServerFieldExtendData;
33  import com.flexstor.flexdbserver.disguise.DisguiseLoader;
34  import com.flexstor.flexdbserver.services.Service;
35  import com.flexstor.flexdbserver.services.ServiceContext;
36  
37  /**
38   * <P>
39   * FileInfoPSDService <BR>
40   * <BLOCKQUOTE>
41   *    This service will obtain the IPTC information from a Photoshop file and place it in
42   *    the metadata based on the configuration specification in the roletype_services.config
43   *    file.
44   * </BLOCKQUOTE>
45   * </P>
46   *
47   * <P>
48   * Configurable Properties in services.config <BR>
49   * <BLOCKQUOTE>
50   *    Asset-file-type/subservice code mapping.  Specific file types must be mapped to specific
51   *    subservice parsers.  The file type name must be the name used in the Typer service definition 
52   *    file.  The currently available parser/type mappings are:
53   *    <BR>
54   *          EPSType  = <file-type-name>
55   *          PSDType  = <file-type-name>
56   *          TIFFType = <file-type-name>
57   *    <BR>
58   * </BLOCKQUOTE>
59   * </P>
60   *
61   * Configurable Properties in roletype_services.config <BR>
62   * <BLOCKQUOTE>
63   *         Any combination of the following properties can be set to correspond
64   *    to an asset or element level field: Caption, CaptionWriter, Headline,
65   *    SpecialInstructions, Keywords,Category, SupplementalCategories,Urgency, 
66   *    Byline, BylineTitle, Credit, Source, ObjectName, DateCreated,City, 
67   *    ProvinceState, CountryName, OriginalTransmissionReference, and CopyrightNotice
68   *    <BR>
69   *    For example: <BR> 
70   *         service2.Keywords = Asset,Asset Notes
71   *    <BR>
72   *    or
73   *    <BR>
74   *         service2.Caption = Element,Notes
75   * </BLOCKQUOTE>
76   *
77   * <!-- THE ACTUAL TABLE:                                                     -->
78   *
79   * <TABLE BORDER="1" CELLPADDING="3" CELLSPACING="3">
80   * <CAPTION ALIGN=TOP>
81   *    <B> In/Out Properties for Assets </B>
82   * </CAPTION>
83   *     <TR>
84   *        <FONT SIZE=+1><B>
85   *        <TH WIDTH="120">Attribute</TH>
86   *                                   <TH WIDTH="30">IN</TH>           <TH WIDTH="30">OUT</TH>          <TH WIDTH="30">Default IN</TH>  <TH WIDTH="30">Default OUT</TH>
87   *        </B></FONT>
88   *     </TR>
89   *     <TR>
90   *        <TH ALIGN=LEFT><FONT SIZE=+1><B>ROLE</B></FONT></TH><TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> ALL </TD>    <TD ALIGN=CENTER> ALL </TD>
91   *     </TR>
92   *     <TR><TH> Highres </TH>        <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
93   *     <TR><TH> Lowres </TH>         <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
94   *     <TR><TH> Thumbnail </TH>      <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
95   *     <TR><TH> Layout </TH>         <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
96   *     <TR><TH> Video </TH>          <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
97   *     <TR><TH> Audio </TH>          <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
98   *     <TR>
99   *        <TH ALIGN=LEFT><FONT SIZE=+1><B>TYPE</B></FONT></TH>
100  *                                   <TD ALIGN=CENTER> X* </TD>    <TD ALIGN=CENTER> X* </TD>    <TD ALIGN=CENTER> ALL* </TD>    <TD ALIGN=CENTER> ALL* </TD></TR>
101  *     </TR>
102  *     <TR>
103  *        <TH ALIGN=LEFT><FONT SIZE=+1><B>FLAG</B></FONT></TH>
104  *     </TR>
105  *     <TR><TH> PARENT </TH>         <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD></TR>
106  *     <TR><TH> CHLDREN </TH>        <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
107  *     <TR><TH> ALL </TH>            <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
108  *     <TR><TH> TEMP_PARENT </TH>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
109  *     <TR><TH> TEMP_CHILDREN </TH>  <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
110  *     <TR><TH> TEMP_ALL </TH>       <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> X </TD>    <TD ALIGN=CENTER> &nbsp </TD>    <TD ALIGN=CENTER> &nbsp </TD></TR>
111  * </TABLE>
112  *    *Note: This service will only operate on Photoshop files.  The service does some byte
113  *    matching similar to the FileTyper to make sure it has a Photoshop file before it even
114  *    attempts to parse the IPTC information from it.  However, the actual Type name is not
115  *    verified and so it could potentially be anything.
116  *
117  * <P>
118  * Input Data Object <BR>
119  * <BLOCKQUOTE>
120  *    com.flexstor.common.importprocessor.ImportData
121  * </BLOCKQUOTE>
122  * </P>
123  *
124  * <P>
125  * Output Data Object <BR>
126  * <BLOCKQUOTE>
127  *    com.flexstor.common.importprocessor.ImportResult
128  * </BLOCKQUOTE>
129  * </P>
130  *
131  */
132  public class FileInfoPSDService
133    implements Service
134 {
135    /** Is this the MKS identifier?   Hmm...
136     */
137    public String IDENTIFIER = "$Id:";
138    
139    protected ServiceContext context;
140 
141    /** A string for holding the service name (set during initData)
142     */
143    private String sThisService = "";
144    
145    /** The final result; whether we were successful.  Start out an optimist.
146     */
147    protected boolean successful = true;
148 
149    /** The import data object.
150     */
151    private ImportData importData = null;
152 
153    /** The disguise extend data contains the field labels, etc., for the disguise.
154     */
155    private ServerDisguiseExtendData refDisguiseExtend = null;
156 
157    /** This hashtable holds the IPTC_dataset_number to Asset_field mapping
158     */
159    private Hashtable hDataToField = new Hashtable();
160    
161    /** This hashtable holds the asset file type to subservice mapping
162    */
163    private Hashtable hAssetToSubservice = new Hashtable();
164    
165    /** These are the possible "File Info..." fields which can be mapped to
166     * asset data fields.  The string at [n][0] is the label as it appears in
167     * Photoshop 5.5.  The number is the IPTC mapped dataset number.
168     */
169    public final String[][] asFileInfoDataItems =
170    {
171       /* CAPTION */
172    {"Caption", "2:120"},
173    {"CaptionWriter", "2:122"},
174    {"Headline", "2:105"},
175    {"SpecialInstructions", "2:40"},
176       /* KEYWORDS */
177    {"Keywords", "2:25"},
178       /* CATEGORIES */
179    {"Category", "2:15"},
180    {"SupplementalCategories", "2:20"},
181    {"Urgency", "2:10"},
182       /* CREDITS */
183    {"Byline", "2:80"},
184    {"BylineTitle", "2:85"},
185    {"Credit", "2:110"},
186    {"Source", "2:115"},
187       /* ORIGIN */
188    {"ObjectName", "2:5"},
189    {"DateCreated", "2:55"},
190    {"City", "2:90"},
191    {"ProvinceState", "2:95"},
192    {"CountryName", "2:101"},
193    {"OriginalTransmissionReference", "2:103"},
194       // "Preserve Additional Information" -- not found in IPTC spec
195       /* COPYRIGHT & URL */
196       // "Mark As Copyrighted" -- not found in IPTC spec
197    {"CopyrightNotice", "2:116"}
198       // "Image URL" -- not found in IPTC spec
199    };
200 
201 
202    /**
203     * Calls before the service is initialized (before initData is called) to 
204     * pass information about the environment in which the service is running.
205     * This environment consists of information about the properties set for the
206     * service in one of these files (services.config, roletype_services.config,
207     * or *.ctl), plus methods to access other information such as an instance
208     * of the service broker to invoke other services, the transaction id for
209     * the service, file separator character and local path for the installation
210     * directory and configuration directory.
211     * 
212     * @param context Holds information about the environment in which the service
213     *                is running.
214     */
215    public void setServiceContext( ServiceContext context )
216    {
217       this.context = context;
218    }
219    
220    /**
221     * Data initialization method called at the beginning of the service.
222     *
223     * @param actionData ActionData is the super class of the data wrapper object
224     *        which contains all the information for executing the service.
225     */
226    public void initData( ActionData actionData )
227    {
228       Diagnostic.trace( Diagnostic.APPSERVER_IMPORT, "Starting service " + sThisService );
229 
230       importData = (ImportData) actionData;
231 
232       // In order to retrieve the bucket extend data, we'll need the disguise record data
233       DisguiseRecordData disguiseRecord = importData.getDisguiseRecordRef();
234       // Once we have the disguise record data, we can get the Disguise Id and Extend data
235       try
236       {
237          refDisguiseExtend = DisguiseLoader.getDisguise( disguiseRecord.getDisguiseId() );
238       }
239       catch( Exception e )
240       {
241          System.out.println( "Exception occurred whilst loading the disguise" );
242          e.printStackTrace();
243       }
244       
245       sThisService = context.getProperty(ServiceArgumentsI.SERVICE_NAME);
246 
247       String sTempPropertyValue = null;
248       for( int i=0; i < asFileInfoDataItems.length; i++ )
249       {
250          sTempPropertyValue = context.getProperty( asFileInfoDataItems[i][0] );
251          if( sTempPropertyValue != null )
252          {
253             hDataToField.put( asFileInfoDataItems[i][1], sTempPropertyValue );
254             Diagnostic.trace( Diagnostic.APPSERVER_IMPORT, 
255                   "Will map " + asFileInfoDataItems[i][0] + 
256                   "(" + asFileInfoDataItems[i][1] + ") to " +
257                   context.getProperty( asFileInfoDataItems[i][0] ) );
258          }
259       }
260    
261       // Get the asset file types to subservice mapping from services.config
262       String sValue = context.getProperty("PSDType");
263       if ((sValue != null) && (sValue.equals("")) == false)
264       {
265          hAssetToSubservice.put(sValue, "PSDType");
266       }
267       
268       sValue = context.getProperty("EPSType");
269       if ((sValue != null) && (sValue.equals("") == false))
270       {
271          hAssetToSubservice.put(sValue, "EPSType");
272       }
273       
274       sValue = context.getProperty("TIFFType");
275       if ((sValue != null) && (sValue.equals("") == false))
276       {
277          hAssetToSubservice.put(sValue, "TIFFType");
278       }
279       
280    } // initData
281 
282 
283 
284    /**
285     * Start of the  Service
286     *
287     * @return a Result object with the an updated data object.
288     */
289    public ActionResult go()
290    {
291 
292       DisguiseRecordData disguiseRecordData = importData.getDisguiseRecordRef();
293       String sRole = context.getProperty(ServiceArgumentsI.ROLE_DATA_SOURCE);
294       String sType = context.getProperty(ServiceArgumentsI.TYPE_DATA_SOURCE);
295       String sFlag = context.getProperty(ServiceArgumentsI.FLAG_DATA_SOURCE);
296 
297       Vector assetRecords =  null;
298 
299       if(disguiseRecordData != null)
300       {
301          if(sRole == null || sType == null || sFlag == null)
302          successful = false;
303          else
304          {
305             assetRecords = disguiseRecordData.getAssets(sRole, sType, sFlag);
306             if(assetRecords != null)
307             {
308                Enumeration assets = assetRecords.elements();
309                while(assets.hasMoreElements())
310                {
311                   // Get the next Asset in line
312                   DisguiseAssetRecordData anAsset = 
313                                  (DisguiseAssetRecordData)assets.nextElement();
314                   
315                   // And process that asset
316                   processAsset( anAsset );
317                }
318             }
319             else
320             {
321                // If there are no assets, this service was not successful.
322                successful = false;
323             }   
324          }
325       }
326       else
327       {
328          // If the disguise could not be loaded, this service could not be successful.
329          successful = false;
330       }
331       
332       // Create the response
333       ImportResult response = new ImportResult(successful);
334       response.setImportData(importData);
335 
336       Diagnostic.trace( Diagnostic.APPSERVER_IMPORT, "Finished service " + sThisService );
337 
338       return response;
339    }
340 
341 
342    /** Cycle through the IPTC info already found and retrieved from a file
343     * and attach it to the Asset
344     */
345    private boolean processAsset( DisguiseAssetRecordData anAsset )
346    {
347       String sCurrentDatasetID   = null;     // This is the 2:XX dataset ID number
348       String sFieldLabel         = null;     // The Field Label from the Disguise
349       String sValue              = null;     // The data value in the file
350       Hashtable hElementDataHash = new Hashtable(); // Holds element data until processing
351 
352       // System.out.println( "The asset can be found at: " +
353       //            anAsset.getServer() + ":/" + anAsset.getLocation() +
354       //            anAsset.getFileName() );
355 
356       // File handle for the asset so we can read it.
357       String sFilePath = "/" + anAsset.getLocation() + anAsset.getFileName();
358       FlexXFile anAssetFile = new FlexXFile( sFilePath );
359 
360       // Get the asset file type so that we can determine the subservice code that
361       // will operate on it based on the mapping data obtained from services.config
362       AssetRoleData roleData = anAsset.getAssetRole();
363       String sAssetFileType  = roleData.getAssetFileType();
364       String sSubServiceType = (String)hAssetToSubservice.get(sAssetFileType);
365       IIPTCParser IptcParser = null;
366       
367       // Select the subservice class, default to PSDParser
368       if ((sSubServiceType == null) || 
369           (sSubServiceType.equals("") == true))
370       {
371           // Type error
372           IptcParser = null;
373       }
374       
375       else if (sSubServiceType.equals("PSDType") == true)
376       {
377           IptcParser = new PSDParser( anAssetFile );
378       }
379       
380       else if (sSubServiceType.equals("EPSType") == true)
381       {
382           IptcParser = new EPSParser( anAssetFile );
383       }
384       
385       else if (sSubServiceType.equals("TIFFType") == true)
386       {
387           IptcParser = new TIFFParser( anAssetFile );
388       }
389       
390       else 
391       {
392           // Type error
393           IptcParser = null;        
394       }
395       
396       System.out.println("Instantiating parser subservice class: " + sSubServiceType + " for " + sFilePath);
397       if (IptcParser == null)
398       {
399           System.out.println("Error instantiating parser subservice class. ");
400           new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, "Iptc subservice parser not specified for file type: " + sAssetFileType);
401           return true;
402       }
403       
404       // The subservice class has been selected and instantiated so now
405       // request it to parse the file
406       if( IptcParser.parseFile() == true )
407       {
408           Diagnostic.trace( Diagnostic.APPSERVER_IMPORT, 
409                               "File Info successfully parsed from file: " + sFilePath);
410       }
411       else
412       {
413           Diagnostic.warn( Diagnostic.APPSERVER_IMPORT,
414                               "File Info not successfully parsed from file: " + sFilePath );
415       }
416 
417       
418       // loop through all of the dataset numbers that the service settings had
419       // mentioned and see if the file we parsed had any of the applicable fields.
420       for( Enumeration e = hDataToField.keys(); e.hasMoreElements(); )
421       {
422          // Retrieve the 2:XX number
423          sCurrentDatasetID = (String) e.nextElement();
424          // Retrieve the Field Label
425          sFieldLabel = (String) hDataToField.get( sCurrentDatasetID );
426          // Retrieve the value that we found in the file
427          sValue = (String) IptcParser.getDatasetValue( sCurrentDatasetID );
428 
429          // If this DataSet/Field does not have a value, don't worry about it.
430          if( sValue != null )
431          {
432             if( sFieldLabel.startsWith( "Asset," ) )
433             {
434                // Strip the "Asset," indicator from the sFieldLabel
435                sFieldLabel = sFieldLabel.substring( 6 );
436                
437                System.out.println( "Asset field '" + sFieldLabel + "' will be: " + sValue );
438 
439                // Add the data to the asset
440                anAsset.addUserData( sFieldLabel, sValue );
441             }
442             else if( sFieldLabel.startsWith( "Element," ) )
443             {
444                // Strip the "Element," indicator from the sFieldLabel
445                sFieldLabel = sFieldLabel.substring( 8 );
446                
447                System.out.println( "Element field '" + sFieldLabel + "' will be: " + sValue );
448                
449                // Adding the data to the element is a little more complicated
450                // Place the data in a hashtable so the data can be added all-at-once
451                hElementDataHash.put( sFieldLabel, sValue );
452             }
453             else
454             {
455                System.out.println( "Unknown bucket level.  Should be Asset or Element." );
456             }
457             
458          }
459       }
460       
461       // This next section will only need to be executed if there is data that will
462       // be placed into Element level fields.
463       if( hElementDataHash.isEmpty() != true )
464       {
465          // This will be the element that contains our asset
466          DisguiseElementRecordData theElement = null;
467          
468          // In order to find the element that this asset is in, get the traversal path
469          Vector vTraversalPath = anAsset.getTraversalPath();
470          // The element is the last item in the vector
471          theElement = 
472            (DisguiseElementRecordData) vTraversalPath.elementAt( vTraversalPath.size()-1 );
473          
474          // Get the extend data for the Element bucket (I hope)
475          ServerBucketExtendData tmpSBED =
476          refDisguiseExtend.getServerBucketExtendDataObject( theElement.getBucketStructId() );
477          
478          // Now retrieve the vector of field data objects
479          Vector vFields = tmpSBED.getFieldDataObjects();
480          
481          // This will probably never happen, but in case it does, it would be nice to know
482          if( vFields == null ) System.out.println( "Unable to obtain the field label data" );
483          
484          // By declaring this variable outside the loop, we don't have to create it each time
485          ServerFieldExtendData tmpSFED = null;
486          String sTmpValue = null;
487 
488          // Get the Field Record Data for the element
489          DisguiseFieldRecordData tmpDFRD [] = theElement.getValues();
490          // Remember this for later if we have new DisguiseFieldRecordDatas (assume false)
491          boolean bSetNewDFRD = false;
492          if( tmpDFRD == null )
493          {
494             tmpDFRD = new DisguiseFieldRecordData[ vFields.size() ];
495             for( int j=0; j < vFields.size(); j++ )
496             {
497                tmpDFRD[j] = new DisguiseFieldRecordData();
498             }
499             // Maybe we should re-attach it to the element...
500             bSetNewDFRD = true;
501          }
502          
503          // Cycle through the field labels, check if we have data for each field
504          for( int i=0; i < vFields.size(); i++ )
505          {
506             // Get a field extend data (for getting the label).
507             tmpSFED = (ServerFieldExtendData) vFields.elementAt(i);
508             
509             // Get the value, if there is one, for that label.
510             sTmpValue = (String) hElementDataHash.get( tmpSFED.getLabel() );
511 
512             // If there is not a new value for this field (==null), just skip it
513             // Otherwise, modify the current value for this field
514             if( sTmpValue != null )
515             {
516                // A disguise field exists for the input Iptc data field (labels match)
517                // Now check to make sure that the input data will fit in the db field
518                if (tmpSFED.getLength() < sTmpValue.length())
519                {
520                   // The disguise (db) field length is smaller than the data we want to
521                   // put into it so we can't update the field and should abort this asset.
522                   System.out.println("Data field overrun " + tmpSFED.getLabel());
523                   new FlexError(FlexError.CRITICAL, sThisService, IDENTIFIER, "Iptc data length is too long for the field " + tmpSFED.getLabel() + ": " + sTmpValue);
524                   return true;
525                }
526                
527                //DEBUG System.out.print( "New value was found for " + tmpSFED.getLabel() );
528                // the DisguiseFieldRecordData method to change values requires an array
529                String asNewValues[] = { sTmpValue };
530                // modify the current data value
531                tmpDFRD[i].setValues( asNewValues );
532                //DEBUG System.out.println( "; now the value is >" + tmpDFRD[i].getValue() + "<" );
533             }
534          } /* end of for loop */
535          
536          if ( bSetNewDFRD == true )
537          {
538             theElement.setValues( tmpDFRD );
539             System.out.println( "Note: element level data may not be modified during GUI import" );
540          }
541          
542       } /* End of element specific field/value stuff */
543 
544       return true;
545    } // processAsset()
546    
547 }