1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 
25 import java.util.*;
26 import java.io.*;
27 
28 public class JPropEx
29 {
30     private String inputFileArg     = "";
31     private String outputFileArg    = "";
32     private String pathPrefixArg    = "";
33     private String pathPostfixArg   = "";
34     private String projectArg       = "";
35     private String rootArg          = "";
36     private Vector forcedLangsArg;
37     private Vector langsArg;
38     private String inputSdfFileArg  = "";
39     private boolean isQuiet             = false;
40     private final String resourceType   = "javaproperties";
41     private final String sourceLanguage = "en-US";
42 
43     static final int JAVA_TYPE      = 0;
44     static final int JAVA_ENUS_TYPE = 1;
45     static final int EXTENSION_TYPE = 2;
46 
JPropEx()47     public JPropEx()
48     {
49         //data = new SdfData();
50     }
51 
JPropEx( String args[] )52     public JPropEx( String args[] )
53     {
54         super();
55         parseArguments( args );
56         testCL();
57         //testArguments();
58         if( inputSdfFileArg != null && inputSdfFileArg.length() > 0 )
59             merge();
60         else
61             extract();
62     }
63 
getSimpleArg( String[] args , int x )64     private String getSimpleArg( String[] args , int x )
65     {
66         if( x < args.length ) x++;
67         else
68         {
69             System.err.println("ERROR: Missing arg for "+args[ x ]+"\n");
70             help();
71         }
72         return args[ x ];
73     }
getComplexArg( String[] args , int x )74     private Vector getComplexArg( String[] args , int x )
75     {
76         if( x < args.length ) x++;
77         else
78         {
79             System.err.println("ERROR: Missing arg for "+args[ x ]+"\n");
80             help();
81         }
82         String value = args[ x ];
83         Vector values = new Vector( Arrays.asList( value.split(",") ) );
84         return values;
85     }
86 
testCL()87     private void testCL()
88     {
89         if( inputFileArg.length()>0 && ( ( pathPrefixArg.length()>0 && pathPostfixArg.length()>0 ) || outputFileArg.length()>0 ) && projectArg.length()>0 && rootArg.length()>0 && langsArg.size()>0 )
90             if( ( inputSdfFileArg.length()>0 && ( outputFileArg.length()>0 ||  ( pathPrefixArg.length()>0 && pathPostfixArg.length()>0 ) ) ) ||  ( inputFileArg.length()>0 && outputFileArg.length()>0 ) )
91                     return;
92         System.out.println("ERROR: Strange parameters!");
93         help();
94         System.exit( -1 );
95     }
help()96     private void help()
97     {
98         System.out.println("jpropex -> extract / merge java properties files");
99         System.out.println("-p <project> -r <project_root> -i <input>|@<input> -o <output> -x <path1> -y <path2> -m <sdf_file> -l <lang>\n");
100         System.out.println("Example:\ncd /data/cws/l10ntooling17/DEV300/ooo/reportbuilder/java/com/sun/star/report/function/metadata");
101         System.out.println("Extract:\njpropex -p reportbuilder -r ../../../../../../.. -i Title-Function.properties -o new.sdf -l en-US");
102         System.out.println("Merge: use either ( -x path -y more_path ) or ( -o ) and ( -i filename ) or ( -i @filename ). @filename contains a list with files");
103         System.out.println("jpropex -p reportbuilder -r ../../../../../../.. -x ../../../../../../../unxlngx6.pro/class/com/sun/star/report/function/metadata -y ivo -i @abc -l all -lf en-US,de,fr,pt -m ../../../../../../../common.pro/misc/reportbuilder/java/com/sun/star/report/function/metadata/localize.sdf");
104         System.out.println("jpropex -p reportbuilder -r ../../../../../../.. -x ../../../../../../../unxlngx6.pro/class/com/sun/star/report/function/metadata -y ivo -i @abc -l all -lf en-US,de,fr,pt -m ../../../../../../../common.pro/misc/reportbuilder/java/com/sun/star/report/function/metadata/localize.sdf");
105         System.out.println("jpropex -p reportbuilder -r ../../../../../../.. -o ../../../../../../../unxlngx6.pro/class/com/sun/star/report/function/metadata/ -i Title-Function.properties -l all -lf en-US,de,fr,pt -m ../../../../../../../common.pro/misc/reportbuilder/java/com/sun/star/report/function/metadata/localize.sdf");
106         System.out.println("jpropex -p reportbuilder -r ../../../../../../.. -x ../../../../../../../unxlngx6.pro/class/com/sun/star/report/function/metadata -y ivooo -i Title-Function.properties -l all -lf en-US,de,fr,pt -m ../../../../../../../common.pro/misc/reportbuilder/java/com/sun/star/report/function/metadata/localize.sdf");
107         System.exit( -1 );
108     }
109 
extract()110     private void extract()
111     {
112         SdfData data = new SdfData();
113         java.util.Properties prop = loadProp( inputFileArg );
114 
115         // Get a prototype that already contains the most common settings
116         SdfEntity dolly = prepareSdfObj( inputFileArg );
117         String key;
118         SdfEntity currentStr;
119         String value;
120         String str;
121         for( Enumeration e = prop.propertyNames() ; e.hasMoreElements() ; )
122         {
123             key         = (String)      e.nextElement();
124             currentStr  = (SdfEntity)   dolly.clone();
125             // Set the new GID and the string text
126             currentStr.setGid( key );
127             value            = prop.getProperty( key , "" );
128             //if( value.equals("") )  System.err.println("Warning: in file "+inputFileArg+" the string with the key "+key+" has a empty string!");
129             str = (prop.getProperty( key )).replaceAll("\t" , " " );    // remove tab
130             str = str.replaceAll("\n"," ");                             // remove return
131             currentStr.setText( str );
132             if( str.length() > 0 )
133                 data.add( currentStr );
134         }
135         data.write( outputFileArg );
136     }
137 
prepareSdfObj( String filename )138     private SdfEntity prepareSdfObj( String filename )
139     {
140         String path = makeAbs( filename ).trim();
141         String myRootArg = makeAbs( rootArg ).trim();
142         myRootArg = myRootArg.replace( "\\","/");
143         myRootArg += "/";
144         path = path.replace("\\","/");
145         path = path.replace( myRootArg, "" );
146         path = path.replace("/","\\");
147         // TODO: Make this static
148         java.text.SimpleDateFormat dateformat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
149         String date = dateformat.format( new Date() );
150         return new SdfEntity( projectArg , path , "0" /* dummy1 */ , resourceType , "", "" , "" , "" , "0" /* dummy2 */ ,
151                               sourceLanguage , "",  "" , ""  , "" , date );
152     }
153 
merge()154     private void merge()
155     {
156         SdfData data = getSdfData();
157         if( inputFileArg.startsWith("@") )
158         {
159             // Read files
160             Vector fileList = readFileList( inputFileArg );
161             for( Enumeration e = fileList.elements(); e.hasMoreElements(); )
162                 mergeFile( (String) e.nextElement() , data , false );
163         }
164         else
165         {
166             // Single file
167             mergeFile( inputFileArg , data , true );
168         }
169     }
170 
readFileList( String filename )171     private Vector readFileList( String filename )
172     {
173         Vector lines = new Vector();
174         try
175         {
176             BufferedReader in = new BufferedReader( new FileReader( filename.substring( 1 ) ) );
177             while( in.ready() )
178                 lines.add( in.readLine().trim() );
179         }
180         catch( IOException e )
181         {
182             System.out.println("ERROR: Can't open file '"+filename.substring( 1 )+"'");
183             System.exit( -1 );
184         }
185         return lines;
186     }
187 
mergeFile( String filename , SdfData data , boolean isSingleFile )188     private void mergeFile( String filename , SdfData data , boolean isSingleFile )
189     {
190         int type = detectFormat( filename );
191         java.util.Properties sourceProp = loadProp( filename );
192         Vector langs = getLanguages( data );
193         HashMap props = new HashMap();
194         // Create a properties object for every language
195         for( Enumeration e = langs.elements(); e.hasMoreElements();)
196         {
197             props.put( (String)e.nextElement() , new java.util.Properties() );
198         }
199         // Get a prototype that already contains the most common settings
200 
201         SdfEntity dolly = prepareSdfObj( filename );
202         String key;
203         String sourceString;
204         SdfEntity curStr;
205         SdfEntity curEntity;
206         SdfEntity mergedEntity;
207         String curLang;
208         for( Enumeration e = sourceProp.propertyNames() ; e.hasMoreElements() ; )     // For all property keys
209         {
210             key          = (String) e.nextElement();
211             sourceString = sourceProp.getProperty( key );
212             curStr       = (SdfEntity) dolly.clone();
213             curStr.setGid( key );
214             for( Enumeration lang = langs.elements(); lang.hasMoreElements(); ) // merge in every language
215             {
216                 curEntity   = (SdfEntity) curStr.clone();
217                 curLang     = (String)    lang.nextElement();
218                 curEntity.setLangid( curLang );
219                 mergedEntity = data.get( curEntity );
220                 if( mergedEntity == null )
221                 {
222                     // in case there is no translation then fallback to the en-US source string
223                     ( (java.util.Properties) props.get( curLang )).setProperty( curEntity.getGid() , sourceString  );
224                 }
225                 else
226                 {
227                     // Set the merged text from the sdf file
228                     ( (java.util.Properties) props.get( curLang )).setProperty( mergedEntity.getGid() , mergedEntity.getText() );  // TODO: Quoting ???
229                 }
230             }
231 
232         }
233         // Now write them out
234         String lang;
235         for( Iterator i = props.keySet().iterator() ; i.hasNext() ; )
236         {
237             lang = (String) i.next();
238             writeSinglePropertiesFile( filename , (java.util.Properties) props.get( lang ) , lang , isSingleFile , type );
239         }
240     }
writeSinglePropertiesFile( String filename , java.util.Properties prop , String lang , boolean isSingleFile , int type )241     private void writeSinglePropertiesFile( String filename , java.util.Properties prop , String lang , boolean isSingleFile , int type )
242     {
243         // Prepare path to file
244         int filenameIdx     = filename.lastIndexOf( "/" ) > 0 ? filename.lastIndexOf( "/" )+1 : 0 ;
245         String path         = new String();
246         String name         = new String();
247         String lcLang       = lang.toLowerCase();
248         // use of -x <path> -y <more_path>
249         // -> <path>/<lang>/<more_path>
250         if( pathPrefixArg != null && pathPrefixArg.length()>0 && pathPostfixArg != null && pathPostfixArg.length()>0 )
251         {
252             path = new StringBuffer().append( pathPrefixArg ).append( "/" ).append( lcLang ).append( "/" ).append( pathPostfixArg ).append( "/" ).toString();
253             name += formatFilename( filename , filenameIdx , lang , type );
254         }
255         //use of -i <one_filename>
256         else if( !isSingleFile && outputFileArg != null && outputFileArg.length()>0 )
257         {
258             //name = outputFileArg;
259             path = outputFileArg;
260             name += formatFilename( filename , filenameIdx , lang , type );
261         }
262         //use of -i @<file_containing_many_filenames>
263         else if( isSingleFile && outputFileArg != null && outputFileArg.length()>0 )
264         {
265             //name = outputFileArg;
266             path = outputFileArg;
267             name += formatFilename( filename , filenameIdx , lang , type );
268         }
269         else
270         {
271             System.err.println("ERROR: No outputfile specified .. either -o or -x -y !");
272             System.exit( -1 );
273         }
274 
275         File dir = new File( path );
276         try
277         {
278             if( !dir.exists() && path.length()>0 )
279             {
280                 if( !dir.mkdirs() )
281                 {
282                     System.out.println("ERROR: Can't create directory '"+path+"' !!!");
283                     System.exit( -1 );
284                 }
285             }
286         }
287         catch( SecurityException e )
288         {
289             System.out.println("ERROR: Can't create directory '"+path+"'!!!Wrong Permissions?");
290             System.exit( -1 );
291         }
292         path += name;
293         // Write the properties file
294         //System.out.println("DBG: Writing to "+path);
295         try{
296             BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream( path ) );
297             if( prop == null )
298                 System.out.println("DBG: prop == null!!!");
299             prop.store( out , "" );     // Legal headers?
300         }
301         catch( IOException e )
302         {
303             System.out.println("ERROR: Can't write file '"+path+"' !!!!");
304             System.exit( -1 );
305         }
306     }
307 
308     // we have different types of properties in the source code
309     // each needs a different file nameing scheme
detectFormat( String filename )310     private int detectFormat( String filename )
311     {
312        if( filename.endsWith( "_en_US.properties" ) )
313            return EXTENSION_TYPE;
314        else if( filename.endsWith("_en_us.properties" ) )
315            return JAVA_ENUS_TYPE;
316        else if( filename.endsWith( ".properties" ) )
317            return JAVA_TYPE;
318 
319        // Can not detect, exit
320        System.err.println("ERROR: Invalid file name. Only allowed (case sensitive!)  *_en_US.properties , *_en_us.properties or *.properties\n");
321        System.exit(-1);
322        return JAVA_TYPE;    // dummy
323     }
324 
formatFilename( String filename , int filenameIdx , String lang , int type )325     private String formatFilename( String filename , int filenameIdx , String lang , int type )
326     {
327 
328         if( !lang.equals( "en-US" ) )
329         {
330             // Parse iso code
331             int pos = lang.indexOf("-");
332             String langpart1 = new String();
333             String langpart2 = new String();
334             if( pos == -1 )
335             {
336                 langpart1 = lang;
337             }
338             else if( pos > 0 )
339             {
340                 langpart1 = lang.substring( 0 , pos );
341                 langpart2 = lang.substring( pos+1 , lang.length() );
342             }
343             // change filename according to the type
344             switch( type )
345             {
346                 // -> de_DE
347                 case EXTENSION_TYPE:
348                     lang  =  langpart1.toLowerCase();
349                     if( langpart2.length() > 0 )                    // -> en_US
350                         lang += "_" + langpart2.toUpperCase();
351                     else                                            // -> de_DE
352                         lang += "_" + langpart1.toUpperCase();
353                     return new StringBuffer().append( filename.substring( filenameIdx , filename.lastIndexOf( "_en_US.properties" ) ) )
354                                         .append( "_" ).append( lang.replaceAll("-","_") ).append( ".properties" ).toString();
355                     // -> de
356                 case JAVA_ENUS_TYPE:
357                     lang = langpart1.toLowerCase();
358                     if( langpart2.length() > 0 )
359                         lang += "_" + langpart2.toLowerCase();
360                     return new StringBuffer().append( filename.substring( filenameIdx , filename.lastIndexOf( "_en_us.properties" ) ) )
361                                         .append( "_" ).append( lang.replaceAll("-","_") ).append( ".properties" ).toString();
362                     // -> de
363                 case JAVA_TYPE:
364                     lang = langpart1.toLowerCase();
365                     if( langpart2.length() > 0 )
366                         lang += "_" + langpart2.toLowerCase();
367                     return new StringBuffer().append( filename.substring( filenameIdx , filename.lastIndexOf( ".properties" ) ) )
368                                         .append( "_" ).append( lang.replaceAll("-","_") ).append( ".properties" ).toString();
369                 default:
370                         System.err.println("ERROR: Something is really broken here, l10ntools/java/jprop/java/JPropEx.java :: formatFilename()");
371                         System.exit( -1 );
372                 break;
373             }
374        }
375         return filename;        // don't change en-US source file name
376     }
getSdfData()377     private SdfData getSdfData()
378     {
379         SdfData data = new SdfData( inputSdfFileArg );
380         data.read();
381         return data;
382     }
getLanguages( SdfData data )383     private Vector getLanguages( SdfData data )
384     {
385         Vector langs = new Vector();
386 
387         if( ((String)langsArg.get( 0 )).equalsIgnoreCase( "all" ) ) // for "-l all" use all languages found in the -m sdf file
388             langs.addAll( data.getLanguages() );
389         else
390             langs.addAll( langsArg );              // use the langs giving by -l
391 
392         if( forcedLangsArg != null )
393             langs.addAll( forcedLangsArg );
394 
395         return removeDupes( langs );
396     }
removeDupes( Vector vec )397     private Vector removeDupes( Vector vec )
398     {
399         Collection coll = new LinkedHashSet( vec );
400         return new Vector( coll );
401     }
loadProp( String filename )402     private java.util.Properties loadProp( String filename )
403     {
404         java.util.Properties prop = new java.util.Properties();
405         try
406         {
407             prop.load( new BufferedInputStream( new NoLocalizeFilter( new FileInputStream( filename ) ) ) );
408         }
409         catch( IOException e )
410         {
411             System.err.println("ERROR: Can't read file '"+filename+"'!!!");
412         }
413         return prop;
414     }
parseArguments( String[] args )415     private void parseArguments( String[] args )
416     {
417 
418         if( args.length == 0 )
419         {
420             System.out.println("ERROR: No args???");
421             help();
422             System.exit( -1 );
423         }
424         for( int x = 0; x < args.length ; x++ )
425         {
426             if( args[ x ].equalsIgnoreCase("-i") )
427 		    {
428                 // Input resource file
429                 inputFileArg = getSimpleArg( args , x );
430             }
431             else if( args[ x ].equalsIgnoreCase("-o") )
432             {
433                 // Output sdf file
434                 outputFileArg = getSimpleArg( args , x );
435             }
436             else if( args[ x ].equalsIgnoreCase("-x") )
437             {
438                 // path prefix
439                 pathPrefixArg = getSimpleArg( args , x );
440             }
441             else if( args[ x ].equalsIgnoreCase("-y") )
442             {
443                 // path postfix
444                 pathPostfixArg = getSimpleArg( args , x );
445             }
446             else if( args[ x ].equalsIgnoreCase("-p") )
447             {
448                 // project
449                 projectArg = getSimpleArg( args , x );
450             }
451             else if( args[ x ].equalsIgnoreCase("-r") )
452             {
453                 // root
454                 rootArg = getSimpleArg( args , x );
455                 rootArg = makeAbs( rootArg );
456             }
457             else if( args[ x ].equalsIgnoreCase("-lf") )
458             {
459                 // forced langs
460                 forcedLangsArg = getComplexArg( args , x );
461             }
462             else if( args[ x ].equalsIgnoreCase("-l") )
463             {
464                 // langs
465                 langsArg = getComplexArg( args , x );
466             }
467             else if( args[ x ].equalsIgnoreCase("-m") )
468             {
469                 // input sdf file
470                 inputSdfFileArg = getSimpleArg( args , x );
471             }
472             else if( args[ x ].equalsIgnoreCase("-qq") )
473             {
474                 isQuiet = true;
475             }
476         }
477     }
makeAbs( String path )478     private String makeAbs( String path )
479     {
480         File file;
481         try
482         {
483             file = new File( path );
484             return file.getCanonicalPath();
485         }catch( IOException e )
486         {
487             e.printStackTrace();
488             System.exit( -1 );
489         }
490         return null;
491     }
492 /*    private boolean testArguments()
493     {
494         // nice merge
495         if( inputSdfFileArg != null && inputSdfFileArg.length()>0 )
496             // nice merge
497             return  projectArg != null  && rootArg != null && inputFileArg != null && pathPrefixArg != null && pathPostfixArg != null && langsArg != null &&
498                     projectArg.length()>0 && rootArg.length()>0 && inputFileArg.length()>0 && pathPrefixArg.length()>0 && pathPostfixArg.length()>0 && langsArg.size()>0 ;
499         else
500             // nice extract
501             return  projectArg != null && rootArg != null && inputFileArg != null && outputFileArg != null && langsArg != null &&
502                     projectArg.length()>0 && rootArg.length()>0 && inputFileArg.length()>0 && outputFileArg.length()>0 && langsArg.size()>0;
503     }
504 */
505 }
506