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