    public ParameterWindow( TableViewer tv ) {
        super( "Table Parameters", tv );
        this.tv = tv;
        this.dataModel = tv.getDataModel();
        this.viewModel = tv.getViewModel();
        this.columnModel = tv.getColumnModel();
        params = new ArrayList();
        pseudoParams = new HashSet();
        uneditableParams = new HashSet();
        DescribedValue nameParam = new DescribedValue( NAME_INFO ) {
            public Object getValue() {
                return dataModel.getName();
            }
            public void setValue( Object value ) {
                String name = value == null ? null : value.toString();
                if ( name.trim().length() == 0 ) {
                    name = null;
                }
                dataModel.setName( name );
            }
        };
        params.add( nameParam );
        pseudoParams.add( nameParam );
        URL url = dataModel.getBaseTable().getURL();
        if ( url != null ) {
            DescribedValue urlParam = new DescribedValue( URL_INFO, url );
            params.add( urlParam );
            pseudoParams.add( urlParam );
            uneditableParams.add( urlParam );
        }
        ncolParam = new DescribedValue( NCOL_INFO );
        nrowParam = new DescribedValue( NROW_INFO );
        params.add( ncolParam );
        params.add( nrowParam );
        pseudoParams.add( ncolParam );
        pseudoParams.add( nrowParam );
        uneditableParams.add( ncolParam );
        uneditableParams.add( nrowParam );
        columnModel.addColumnModelListener( new TableColumnModelAdapter() {
            public void columnAdded( TableColumnModelEvent evt ) {
                configureColumnCount();
            }
            public void columnRemoved( TableColumnModelEvent evt ) {
                configureColumnCount();
            }
        } );
        viewModel.addTableModelListener( new TableModelListener() {
            public void tableChanged( TableModelEvent evt ) {
                configureRowCount();
            }
        } );
        ncolRowIndex = params.indexOf( ncolParam );
        nrowRowIndex = params.indexOf( nrowParam );
        for ( Iterator it = dataModel.getParameters().iterator();
              it.hasNext(); ) {
            DescribedValue param = (DescribedValue) it.next();
            params.add( param );
        }
        List metas = new ArrayList();
        metas.add( new MetaColumn( "Name", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getName();
            }
        } );
        metas.add( new MetaColumn( "Value", Object.class ) {
            public Object getValue( int irow ) {
                return getParam( irow ).getValue();
            }
            public boolean isEditable( int irow ) {
                return isEditableParameter( irow );
            }
            public void setValue( int irow, Object value ) {
                getParam( irow ).setValue( value );
            }
        } );
        int classPos = metas.size();
        metas.add( new MetaColumn( "Class", String.class ) {
            public Object getValue( int irow ) {
                return DefaultValueInfo
                      .formatClass( getParamInfo( irow ).getContentClass() );
            }
        } );
        metas.add( new MetaColumn( "Shape", String.class ) {
            public Object getValue( int irow ) {
                return DefaultValueInfo
                      .formatShape( getParamInfo( irow ).getShape() );
            }
        } );
        metas.add( new MetaColumn( "Units", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getUnitString();
            }
            public boolean isEditable( int irow ) {
                return ! isPseudoParameter( irow ) &&
                       getParamInfo( irow ) instanceof DefaultValueInfo;
            }
            public void setValue( int irow, Object value ) {
                ((DefaultValueInfo) getParamInfo( irow ))
               .setUnitString( value == null ? null : value.toString() );
            }
        } );
        metas.add( new MetaColumn( "Description", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getDescription();
            }
            public boolean isEditable( int irow ) {
                return ! isPseudoParameter( irow ) &&
                       getParamInfo( irow ) instanceof DefaultValueInfo;
            }
            public void setValue( int irow, Object value ) {
                ((DefaultValueInfo) getParamInfo( irow ))
               .setDescription( value == null ? null : value.toString() );
            }
        } );
        metas.add( new MetaColumn( "UCD", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getUCD();
            }
            public boolean isEditable( int irow ) {
                return ! isPseudoParameter( irow ) &&
                       getParamInfo( irow ) instanceof DefaultValueInfo;
            }
            public void setValue( int irow, Object value ) {
                ((DefaultValueInfo) getParamInfo( irow ))
               .setUCD( value == null ? null : value.toString() );
            }
        } );
        metas.add( new MetaColumn( "UCD description", String.class ) {
            public Object getValue( int irow ) {
                String ucdid = getParamInfo( irow ).getUCD();
                if ( ucdid != null ) {
                    UCD ucd = UCD.getUCD( dataModel.getColumnInfo( irow )
                                                   .getUCD() );
                    if ( ucd != null ) {
                        return ucd.getDescription();
                    }
                }
                return null;
            }
        } );
        metaTableModel = new MetaColumnTableModel( metas ) {
            public int getRowCount() {
                return params.size();
            }
        };
        JTable jtab = new JTable( metaTableModel );
        jtab.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        jtab.setColumnSelectionAllowed( false );
        jtab.setRowSelectionAllowed( true );
        StarJTable.configureColumnWidths( jtab, 20000, 100 );
        MetaColumnModel metaColumnModel =
            new MetaColumnModel( jtab.getColumnModel(), metaTableModel );
        metaColumnModel.purgeEmptyColumns();
        jtab.setColumnModel( metaColumnModel );
        metaColumnModel.removeColumn( classPos );
        configureColumnCount();
        configureRowCount();
        getMainArea().add( new SizingScrollPane( jtab ) );
        setMainHeading( "Table Metadata" );
        JMenu displayMenu = metaColumnModel.makeCheckBoxMenu( "Display" );
        displayMenu.setMnemonic( KeyEvent.VK_D );
        getJMenuBar().add( displayMenu );
        addHelp( "ParameterWindow" );
        pack();
        setVisible( true );
    }

    public ParameterWindow( TableViewer tv ) {
        super( "Table Parameters", tv );
        this.tv = tv;
        this.dataModel = tv.getDataModel();
        this.viewModel = tv.getViewModel();
        this.columnModel = tv.getColumnModel();
        params = new ArrayList();
        pseudoParams = new HashSet();
        uneditableParams = new HashSet();
        DescribedValue nameParam = new DescribedValue( NAME_INFO ) {
            public Object getValue() {
                return dataModel.getName();
            }
            public void setValue( Object value ) {
                String name = value == null ? null : value.toString();
                if ( name.trim().length() == 0 ) {
                    name = null;
                }
                dataModel.setName( name );
            }
        };
        params.add( nameParam );
        pseudoParams.add( nameParam );
        URL url = dataModel.getBaseTable().getURL();
        if ( url != null ) {
            DescribedValue urlParam = new DescribedValue( URL_INFO, url );
            params.add( urlParam );
            pseudoParams.add( urlParam );
            uneditableParams.add( urlParam );
        }
        ncolParam = new DescribedValue( NCOL_INFO );
        nrowParam = new DescribedValue( NROW_INFO );
        params.add( ncolParam );
        params.add( nrowParam );
        pseudoParams.add( ncolParam );
        pseudoParams.add( nrowParam );
        uneditableParams.add( ncolParam );
        uneditableParams.add( nrowParam );
        columnModel.addColumnModelListener( new TableColumnModelAdapter() {
            public void columnAdded( TableColumnModelEvent evt ) {
                configureColumnCount();
            }
            public void columnRemoved( TableColumnModelEvent evt ) {
                configureColumnCount();
            }
        } );
        viewModel.addTableModelListener( new TableModelListener() {
            public void tableChanged( TableModelEvent evt ) {
                configureRowCount();
            }
        } );
        ncolRowIndex = params.indexOf( ncolParam );
        nrowRowIndex = params.indexOf( nrowParam );
        for ( Iterator it = dataModel.getParameters().iterator();
              it.hasNext(); ) {
            DescribedValue param = (DescribedValue) it.next();
            params.add( param );
        }
        List metas = new ArrayList();
        metas.add( new MetaColumn( "Name", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getName();
            }
        } );
        metas.add( new MetaColumn( "Value", Object.class ) {
            public Object getValue( int irow ) {
                return getParam( irow ).getValue();
            }
            public boolean isEditable( int irow ) {
                return isEditableParameter( irow );
            }
            public void setValue( int irow, Object value ) {
                getParam( irow ).setValue( value );
            }
        } );
        int classPos = metas.size();
        metas.add( new MetaColumn( "Class", String.class ) {
            public Object getValue( int irow ) {
                return DefaultValueInfo
                      .formatClass( getParamInfo( irow ).getContentClass() );
            }
        } );
        metas.add( new MetaColumn( "Shape", String.class ) {
            public Object getValue( int irow ) {
                return DefaultValueInfo
                      .formatShape( getParamInfo( irow ).getShape() );
            }
        } );
        metas.add( new MetaColumn( "Units", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getUnitString();
            }
            public boolean isEditable( int irow ) {
                return ! isPseudoParameter( irow ) &&
                       getParamInfo( irow ) instanceof DefaultValueInfo;
            }
            public void setValue( int irow, Object value ) {
                ((DefaultValueInfo) getParamInfo( irow ))
               .setUnitString( value == null ? null : value.toString() );
            }
        } );
        metas.add( new MetaColumn( "Description", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getDescription();
            }
            public boolean isEditable( int irow ) {
                return ! isPseudoParameter( irow ) &&
                       getParamInfo( irow ) instanceof DefaultValueInfo;
            }
            public void setValue( int irow, Object value ) {
                ((DefaultValueInfo) getParamInfo( irow ))
               .setDescription( value == null ? null : value.toString() );
            }
        } );
        metas.add( new MetaColumn( "UCD", String.class ) {
            public Object getValue( int irow ) {
                return getParamInfo( irow ).getUCD();
            }
            public boolean isEditable( int irow ) {
                return ! isPseudoParameter( irow ) &&
                       getParamInfo( irow ) instanceof DefaultValueInfo;
            }
            public void setValue( int irow, Object value ) {
                ((DefaultValueInfo) getParamInfo( irow ))
               .setUCD( value == null ? null : value.toString() );
            }
        } );
        metas.add( new MetaColumn( "UCD description", String.class ) {
            public Object getValue( int irow ) {
                String ucdid = getParamInfo( irow ).getUCD();
                if ( ucdid != null ) {
                    UCD ucd = UCD.getUCD( ucdid );
                    if ( ucd != null ) {
                        return ucd.getDescription();
                    }
                }
                return null;
            }
        } );
        metaTableModel = new MetaColumnTableModel( metas ) {
            public int getRowCount() {
                return params.size();
            }
        };
        JTable jtab = new JTable( metaTableModel );
        jtab.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
        jtab.setColumnSelectionAllowed( false );
        jtab.setRowSelectionAllowed( true );
        StarJTable.configureColumnWidths( jtab, 20000, 100 );
        MetaColumnModel metaColumnModel =
            new MetaColumnModel( jtab.getColumnModel(), metaTableModel );
        metaColumnModel.purgeEmptyColumns();
        jtab.setColumnModel( metaColumnModel );
        metaColumnModel.removeColumn( classPos );
        configureColumnCount();
        configureRowCount();
        getMainArea().add( new SizingScrollPane( jtab ) );
        setMainHeading( "Table Metadata" );
        JMenu displayMenu = metaColumnModel.makeCheckBoxMenu( "Display" );
        displayMenu.setMnemonic( KeyEvent.VK_D );
        getJMenuBar().add( displayMenu );
        addHelp( "ParameterWindow" );
        pack();
        setVisible( true );
    }
