001/*
002 * $RCSfile: ModuleSpec.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:01:58 $
005 * $State: Exp $
006 *
007 * Class:                   ModuleSpec
008 *
009 * Description:             Generic class for storing module specs
010 *
011 *                           from WTFilterSpec (Diego Santa Cruz)
012 *
013 * COPYRIGHT:
014 *
015 * This software module was originally developed by Raphaël Grosbois and
016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
019 * Centre France S.A) in the course of development of the JPEG2000
020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
021 * software module is an implementation of a part of the JPEG 2000
022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
024 * Partners) agree not to assert against ISO/IEC and users of the JPEG
025 * 2000 Standard (Users) any of their rights under the copyright, not
026 * including other intellectual property rights, for this software module
027 * with respect to the usage by ISO/IEC and Users of this software module
028 * or modifications thereof for use in hardware or software products
029 * claiming conformance to the JPEG 2000 Standard. Those intending to use
030 * this software module in hardware or software products are advised that
031 * their use may infringe existing patents. The original developers of
032 * this software module, JJ2000 Partners and ISO/IEC assume no liability
033 * for use of this software module or modifications thereof. No license
034 * or right to this software module is granted for non JPEG 2000 Standard
035 * conforming products. JJ2000 Partners have full right to use this
036 * software module for his/her own purpose, assign or donate this
037 * software module to any third party and to inhibit third parties from
038 * using this software module for non JPEG 2000 Standard conforming
039 * products. This copyright notice must be included in all copies or
040 * derivative works of this software module.
041 *
042 * Copyright (c) 1999/2000 JJ2000 Partners.
043 * */
044package jj2000.j2k;
045
046import java.awt.Point;
047import java.util.Enumeration;
048import java.util.Hashtable;
049
050/**
051 * This generic class is used to handle values to be used by a module for each
052 * tile and component.  It uses attribute to determine which value to use. It
053 * should be extended by each module needing this feature.
054 *
055 * This class might be used for values that are only tile specific or
056 * component specific but not both.
057 *
058 * <P>The attributes to use are defined by a hierarchy. The hierarchy is:
059 *
060 * <ul>
061 * <li> Tile and component specific attribute</li>
062 * <li> Tile specific default attribute</li>
063 * <li> Component main default attribute</li>
064 * <li> Main default attribute</li>
065 * </ul>
066 * */
067
068public class ModuleSpec implements Cloneable {
069
070    /** The identifier for a specification module that applies only to
071     * components */
072    public final static byte SPEC_TYPE_COMP = 0;
073
074    /** The identifier for a specification module that applies only to
075        tiles */
076    public final static byte SPEC_TYPE_TILE = 1;
077
078    /** The identifier for a specification module that applies both to
079     * tiles and components */
080    public final static byte SPEC_TYPE_TILE_COMP = 2;
081
082    /** The identifier for default specification */
083    public final static byte SPEC_DEF = 0;
084
085    /** The identifier for "component default" specification */
086    public final static byte SPEC_COMP_DEF = 1;
087
088    /** The identifier for "tile default" specification */
089    public final static byte SPEC_TILE_DEF = 2;
090
091    /** The identifier for a "tile-component" specification */
092    public final static byte SPEC_TILE_COMP = 3;
093
094    /** The type of the specification module */
095    protected int specType;
096
097    /** The number of tiles */
098    protected int nTiles = 0;
099
100    /** The number of components */
101    protected int nComp = 0;
102
103    /** The spec type for each tile-component. The first index is
104     * the tile index, the second is the component index.
105     */
106    protected byte[][] specValType;
107
108    /** Default value for each tile-component */
109    protected Object def = null;
110
111    /** The default value for each component. Null if no component
112        specific value is defined */
113    protected Object[] compDef = null;
114
115    /** The default value for each tile. Null if no tile specific
116        value is defined */
117    protected Object[] tileDef = null;
118
119    /** The specific value for each tile-component. Value of tile 16 component
120     * 3 is accessible through the hash value "t16c3". Null if no
121     * tile-component specific value is defined */
122    protected Hashtable tileCompVal;
123
124    /** The specified value in string format */
125    protected String specified;
126
127    public ModuleSpec getCopy() {
128        return (ModuleSpec)this.clone();
129    }
130
131    /**
132     * Constructs a 'ModuleSpec' object, initializing all the components and
133     * tiles to the 'SPEC_DEF' spec val type, for the specified number of
134     * components and tiles.
135     *
136     * @param nt The number of tiles
137     *
138     * @param nc The number of components
139     *
140     * @param type the type of the specification module i.e. tile specific,
141     * component specific or both.
142     * */
143    public ModuleSpec(int nt, int nc, byte type) {
144
145        nTiles = nt;
146        nComp = nc;
147        specValType = new byte[nt][nc];
148        switch (type) {
149        case SPEC_TYPE_TILE:
150            specType = SPEC_TYPE_TILE;
151            break;
152        case SPEC_TYPE_COMP:
153            specType = SPEC_TYPE_COMP;
154            break;
155        case SPEC_TYPE_TILE_COMP:
156            specType = SPEC_TYPE_TILE_COMP;
157            break;
158        }
159    }
160
161    protected Object clone() {
162        ModuleSpec ms;
163        try {
164            ms = (ModuleSpec)super.clone();
165        } catch(CloneNotSupportedException e) {
166            throw new Error("Error when cloning ModuleSpec instance");
167        }
168        // Create a copy of the specValType array
169        ms.specValType = new byte[nTiles][nComp];
170        for(int t=0; t<nTiles; t++) {
171            for(int c=0; c<nComp; c++) {
172                ms.specValType[t][c] = specValType[t][c];
173            }
174        }
175        // Create a copy of tileDef
176        if(tileDef!=null) {
177            ms.tileDef = new Object[nTiles];
178            for(int t=0; t<nTiles; t++) {
179                ms.tileDef[t] = tileDef[t];
180            }
181        }
182        // Create a copy of tileCompVal
183        if(tileCompVal!=null) {
184            ms.tileCompVal = new Hashtable();
185            String tmpKey;
186            Object tmpVal;
187            for(Enumeration e=tileCompVal.keys(); e.hasMoreElements(); ) {
188                tmpKey = (String)e.nextElement();
189                tmpVal = tileCompVal.get(tmpKey);
190                ms.tileCompVal.put(tmpKey,tmpVal);
191            }
192        }
193        return ms;
194    }
195
196    /** 
197     * Rotate the ModuleSpec instance by 90 degrees (this modifies only tile
198     * and tile-component specifications).
199     *
200     * @param nT Number of tiles along horizontal and vertical axis after
201     * rotation. 
202     * */
203    public void rotate90(Point anT) {
204        // Rotate specValType
205        byte[][] tmpsvt = new byte[nTiles][];
206        int ax,ay;
207        Point bnT = new Point(anT.y,anT.x);
208        for(int by=0; by<bnT.y; by++) {
209            for(int bx=0; bx<bnT.x; bx++) {
210                ay = bx;
211                ax = bnT.y-by-1;
212                tmpsvt[ay*anT.x+ax] = specValType[by*bnT.x+bx];
213            }
214        }
215        specValType = tmpsvt;
216
217        // Rotate tileDef
218        if(tileDef!=null) {
219            Object[] tmptd = new Object[nTiles];
220            for(int by=0; by<bnT.y; by++) {
221                for(int bx=0; bx<bnT.x; bx++) {
222                    ay = bx;
223                    ax = bnT.y-by-1;
224                    tmptd[ay*anT.x+ax] = tileDef[by*bnT.x+bx];
225                }
226            }
227            tileDef = tmptd;
228        }
229
230        // Rotate tileCompVal
231        if(tileCompVal!=null && tileCompVal.size()>0) {
232            Hashtable tmptcv = new Hashtable();
233            String tmpKey;
234            Object tmpVal;
235            int btIdx,atIdx;
236            int i1,i2;
237            int bx,by;
238            for(Enumeration e=tileCompVal.keys(); e.hasMoreElements(); ) {
239                tmpKey = (String)e.nextElement();
240                tmpVal = tileCompVal.get(tmpKey);
241                i1 = tmpKey.indexOf('t');
242                i2 = tmpKey.indexOf('c');
243                btIdx = (new Integer(tmpKey.substring(i1+1,i2))).intValue();
244                bx = btIdx%bnT.x;
245                by = btIdx/bnT.x;
246                ay = bx;
247                ax = bnT.y-by-1;
248                atIdx = ax+ay*anT.x;
249                tmptcv.put("t"+atIdx+tmpKey.substring(i2),tmpVal);
250            }
251            tileCompVal = tmptcv;
252        }
253    }
254
255    /**
256     * Sets default value for this module
257     * */
258    public void setDefault(Object value){
259        def = value;
260    }
261
262    /**
263     * Gets default value for this module.
264     *
265     * @return The default value (Must be casted before use)
266     * */
267    public Object getDefault(){
268        return def;
269    }
270
271    /**
272     * Sets default value for specified component and specValType tag if
273     * allowed by its priority.
274     *
275     * @param c Component index
276     * */
277    public void setCompDef(int c, Object value){
278        if ( specType == SPEC_TYPE_TILE ) {
279            String errMsg = "Option whose value is '"+value+"' cannot be "
280                +"specified for components as it is a 'tile only' specific "
281                +"option";
282            throw new Error(errMsg);
283        }
284        if(compDef==null)
285            compDef = new Object[nComp];
286        for(int i=0; i<nTiles; i++){
287            if(specValType[i][c]<SPEC_COMP_DEF) {
288                specValType[i][c] = SPEC_COMP_DEF;
289            }
290        }
291        compDef[c] = value;
292    }
293
294    /**
295     * Gets default value of the specified component. If no specification have
296     * been entered for this component, returns default value.
297     *
298     * @param c Component index
299     *
300     * @return The default value for this component (Must be casted before
301     * use)
302     *
303     * @see #setCompDef
304     * */
305    public Object getCompDef(int c){
306        if ( specType == SPEC_TYPE_TILE ) {
307            throw new Error("Illegal use of ModuleSpec class");
308        }
309        if(compDef==null || compDef[c]==null){
310            return getDefault();
311        }
312        else
313            return compDef[c];
314    }
315
316    /**
317     * Sets default value for specified tile and specValType tag if
318     * allowed by its priority.
319     *
320     * @param c Tile index.
321     * */
322    public void setTileDef(int t, Object value){
323        if ( specType == SPEC_TYPE_COMP ) {
324            String errMsg = "Option whose value is '"+value+"' cannot be "
325                + "specified for tiles as it is a 'component only' specific "
326                + "option";
327            throw new Error(errMsg);
328        }
329        if(tileDef==null)
330            tileDef = new Object[nTiles];
331        for(int i=0; i<nComp; i++){
332            if(specValType[t][i]<SPEC_TILE_DEF){
333                specValType[t][i] = SPEC_TILE_DEF;
334            }
335        }
336        tileDef[t] = value;
337    }
338
339    /**
340     * Gets default value of the specified tile. If no specification
341     * has been entered, it returns the default value.
342     *
343     * @param t Tile index
344     *
345     * @return The default value for this tile (Must be casted before use)
346     *
347     * @see #setTileDef
348     * */
349    public Object getTileDef(int t){
350        if ( specType == SPEC_TYPE_COMP ) {
351            throw new Error("Illegal use of ModuleSpec class");
352        }
353        if(tileDef==null || tileDef[t]==null){
354            return getDefault();
355        }
356        else
357            return tileDef[t];
358    }
359
360    /**
361     * Sets value for specified tile-component.
362     *
363     * @param t Tie index
364     *
365     * @param c Component index
366     * */
367    public void setTileCompVal(int t,int c, Object value){
368        if ( specType != SPEC_TYPE_TILE_COMP ) {
369            String errMsg = "Option whose value is '"+value+"' cannot be "
370                + "specified for ";
371            switch (specType) {
372            case SPEC_TYPE_TILE:
373                errMsg += "components as it is a 'tile only' specific option";
374                break;
375            case SPEC_TYPE_COMP:
376                errMsg += "tiles as it is a 'component only' specific option";
377                break;
378            }
379            throw new Error(errMsg);
380        }
381        if(tileCompVal==null)
382            tileCompVal = new Hashtable();
383        specValType[t][c] = SPEC_TILE_COMP;
384        tileCompVal.put("t"+t+"c"+c,value);
385    }
386
387    /**
388     * Gets value of specified tile-component. This method calls getSpec but
389     * has a public access.
390     *
391     * @param t Tile index
392     *
393     * @param c Component index
394     *
395     * @return The value of this tile-component (Must be casted before use)
396     *
397     * @see #setTileCompVal
398     *
399     * @see #getSpec
400     * */
401    public Object getTileCompVal(int t,int c){
402        if ( specType != SPEC_TYPE_TILE_COMP ) {
403            throw new Error("Illegal use of ModuleSpec class");
404        }
405        return getSpec(t,c);
406    }
407
408    /**
409     * Gets value of specified tile-component without knowing if a
410     * specific tile-component value has been previously entered. It
411     * first check if a tile-component specific value has been
412     * entered, then if a tile specific value exist, then if a
413     * component specific value exist. If not the default value is
414     * returned.
415     *
416     * @param t Tile index
417     *
418     * @param c Component index
419     *
420     * @return Value for this tile component.
421     * */
422    protected Object getSpec(int t,int c){
423        switch(specValType[t][c]){
424        case SPEC_DEF:
425            return getDefault();
426        case SPEC_COMP_DEF:
427            return getCompDef(c);
428        case SPEC_TILE_DEF:
429            return getTileDef(t);
430        case SPEC_TILE_COMP:
431            return tileCompVal.get("t"+t+"c"+c);
432        default:
433            throw new IllegalArgumentException("Not recognized spec type");
434        }
435    }
436
437    /**
438     * Return the spec type of the given tile-component.
439     *
440     * @param t Tile index
441     *
442     * @param c Component index
443     * */
444    public byte getSpecValType(int t,int c){
445        return specValType[t][c];
446    }
447
448    /**
449     * Whether or not specifications have been entered for the given
450     * component.
451     *
452     * @param c Index of the component
453     *
454     * @return True if component specification has been defined
455     * */
456    public boolean isCompSpecified(int c){
457        if(compDef==null || compDef[c]==null)
458            return false;
459        else
460            return true;
461    }
462
463    /**
464     * Whether or not specifications have been entered for the given
465     * tile.
466     *
467     * @param t Index of the tile
468     *
469     * @return True if tile specification has been entered
470     * */
471    public boolean isTileSpecified(int t){
472        if(tileDef==null || tileDef[t]==null)
473            return false;
474        else
475            return true;
476    }
477
478    /**
479     * Whether or not a tile-component specification has been defined
480     *
481     * @param t Tile index
482     *
483     * @param c Component index
484     *
485     * @return True if a tile-component specification has been defined.
486     * */
487    public boolean isTileCompSpecified(int t,int c){
488        if(tileCompVal==null || tileCompVal.get("t"+t+"c"+c)==null)
489            return false;
490        else
491            return true;
492    }
493
494    /**
495     * This method is responsible of parsing tile indexes set and
496     * component indexes set for an option. Such an argument must
497     * follow the following policy:<br>
498     *
499     * <tt>t\<indexes set\></tt> or <tt>c\<indexes set\></tt> where
500     * tile or component indexes are separated by commas or a
501     * dashes.
502     *
503     * <p><u>Example:</u><br>
504     * <li> <tt>t0,3,4</tt> means tiles with indexes 0, 3 and 4.<br>
505     * <li> <tt>t2-4</tt> means tiles with indexes 2,3 and 4.<br>
506     *
507     * It returns a boolean array skteching which tile or component are
508     * concerned by the next parameters.
509     *
510     * @param word The word to parse.
511     *
512     * @param maxIdx Maximum authorized index
513     *
514     * @return Indexes concerned by this parameter.
515     * */
516    public static final boolean[] parseIdx(String word, int maxIdx){
517        int nChar = word.length(); // Number of characters
518        char c = word.charAt(0);   // current character
519        int idx = -1;              // Current (tile or component) index
520        int lastIdx = -1;          // Last (tile or component) index
521        boolean isDash = false;    // Whether or not last separator was a dash
522
523        boolean[] idxSet = new boolean[maxIdx];
524        int i=1; // index of the current character
525
526        while(i<nChar){
527            c = word.charAt(i);
528            if(Character.isDigit(c)){
529                if(idx==-1)
530                    idx = 0;
531                idx = idx*10+ (c-'0');
532            }
533            else{
534                if(idx==-1 || (c!=',' && c!='-')){
535                   throw new IllegalArgumentException("Bad construction for "+
536                                                      "parameter: "+word);
537                }
538                if(idx<0 || idx>=maxIdx){
539                    throw new IllegalArgumentException("Out of range index in "+
540                                                       "parameter `"+word+"' : "+
541                                                       +idx);
542                }
543
544                // Found a comma
545                if(c==','){
546                    if(isDash){ // Previously found a dash, fill idxSet
547                        for(int j=lastIdx+1; j<idx; j++){
548                            idxSet[j] = true;
549                        }
550                    }
551                    isDash = false;
552                }
553                else // Found a dash
554                    isDash = true;
555
556                // Udate idxSet
557                idxSet[idx] = true;
558                lastIdx = idx;
559                idx=-1;
560            }
561            i++;
562        }
563
564        // Process last found index
565        if(idx<0 || idx>=maxIdx){
566            throw new IllegalArgumentException("Out of range index in "+
567                                               "parameter `"+word+"' : "+idx);
568        }
569        if(isDash)
570            for(int j=lastIdx+1; j<idx; j++){
571                idxSet[j] = true;
572            }
573        idxSet[idx] = true;
574
575        return idxSet;
576    }
577
578    /**
579     * Returns a tile-component representative using default value.
580     *
581     * @return Tile component index in an array (first element: tile
582     * index, second element: component index).
583     * */
584/*
585    public int[] getDefRep(){
586        int[] tcidx = new int[2];
587        for(int t=nTiles-1; t>=0; t--){
588            for(int c=nComp-1; c>=0; c--){
589                if(specValType[t][c]==SPEC_DEF){
590                    tcidx[0] = t;
591                    tcidx[1] = c;
592                    return tcidx;
593                }
594            }
595        }
596
597        throw new IllegalArgumentException("No representative for "+
598                                           "default value");
599    }
600*/
601    /**
602     * Returns a component representative using tile default value.
603     *
604     * @param t Tile index
605     *
606     * @return component index of the representant
607     * */
608/*
609    public int getTileDefRep(int t){
610        for(int c=nComp-1; c>=0; c--)
611            if(specValType[t][c]==SPEC_TILE_DEF){
612                return c;
613            }
614
615        throw new IllegalArgumentException("No representative for tile "+
616                                           "default value");
617    }
618*/
619    /**
620     * Returns a tile representative using component default value.
621     *
622     * @param c Component index
623     *
624     * @return tile index of the representant
625     * */
626/*
627    public int getCompDefRep(int c){
628        for(int t=nTiles-1; t>=0; t--) {
629            if(specValType[t][c]==SPEC_COMP_DEF){
630                return t;
631            }
632        }
633
634        throw new IllegalArgumentException("No representative for component "+
635                                           "default value, c="+c);
636    }
637*/
638/*
639    public String getSpecified() {
640        return specified;
641    }
642*/
643}