001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view.dialog;
016
017import java.io.InputStream;
018import java.math.BigInteger;
019import java.net.URL;
020import java.net.URLClassLoader;
021import java.util.Iterator;
022import java.util.List;
023import java.util.Scanner;
024import java.util.StringTokenizer;
025
026import org.eclipse.swt.SWT;
027import org.eclipse.swt.browser.Browser;
028import org.eclipse.swt.events.DisposeEvent;
029import org.eclipse.swt.events.DisposeListener;
030import org.eclipse.swt.events.SelectionAdapter;
031import org.eclipse.swt.events.SelectionEvent;
032import org.eclipse.swt.graphics.Point;
033import org.eclipse.swt.graphics.Rectangle;
034import org.eclipse.swt.layout.GridData;
035import org.eclipse.swt.layout.GridLayout;
036import org.eclipse.swt.widgets.Button;
037import org.eclipse.swt.widgets.Combo;
038import org.eclipse.swt.widgets.Composite;
039import org.eclipse.swt.widgets.Dialog;
040import org.eclipse.swt.widgets.Display;
041import org.eclipse.swt.widgets.Label;
042import org.eclipse.swt.widgets.Shell;
043import org.eclipse.swt.widgets.Text;
044
045import hdf.object.Attribute;
046import hdf.object.Datatype;
047import hdf.object.Group;
048import hdf.object.HObject;
049import hdf.object.MetaDataContainer;
050import hdf.view.Tools;
051import hdf.view.ViewProperties;
052
053/**
054 * NewAttributeDialog displays components for adding a new attribute.
055 *
056 * @author Jordan T. Henderson
057 * @version 2.4 1/7/2016
058 */
059public class NewAttributeDialog extends NewDataObjectDialog {
060
061    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(NewAttributeDialog.class);
062
063    /** the default length of a string attribute */
064    public static final int   DEFAULT_STRING_ATTRIBUTE_LENGTH = 256;
065
066    /** TextField for entering the name of the dataset */
067    private Text              nameField;
068
069    /** TextField for entering the attribute value. */
070    private Text              valueField;
071
072    /** The Choice of the object list */
073    private Combo             objChoice;
074
075    private Button            h4GrAttrRadioButton;
076
077    private Label             arrayLengthLabel;
078
079    /**
080     * Constructs a NewAttributeDialog with specified object (dataset, group, or
081     * image) for the new attribute to be attached to.
082     *
083     * @param parent
084     *            the parent shell of the dialog
085     * @param obj
086     *            the object for the attribute to be attached to.
087     * @param objs
088     *            the specified objects.
089     */
090    public NewAttributeDialog(Shell parent, HObject obj, List<HObject> objs) {
091        super(parent, obj, objs);
092    }
093
094    public void open() {
095        Shell parent = getParent();
096        shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
097        shell.setFont(curFont);
098        shell.setText("New Attribute...");
099        shell.setImage(ViewProperties.getHdfIcon());
100        shell.setLayout(new GridLayout(1, true));
101
102
103        // Create content region
104        Composite content = new Composite(shell, SWT.NONE);
105        content.setLayout(new GridLayout(2, false));
106        content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
107
108        Label label = new Label(content, SWT.LEFT);
109        label.setFont(curFont);
110        label.setText("Name: ");
111
112        nameField = new Text(content, SWT.SINGLE | SWT.BORDER);
113        nameField.setFont(curFont);
114        nameField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
115
116        label = new Label(content, SWT.LEFT);
117        label.setFont(curFont);
118        label.setText("Type: ");
119
120        Composite optionsComposite = new Composite(content, SWT.NONE);
121        optionsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
122        optionsComposite.setLayout(new GridLayout(
123                (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot()) ? 5 : 3,
124                        false)
125                );
126
127        // Dummy label
128        label = new Label(optionsComposite, SWT.LEFT);
129        label.setFont(curFont);
130        label.setText("");
131
132        if (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot()) {
133            label = new Label(optionsComposite, SWT.LEFT);
134            label.setFont(curFont);
135            label.setText("");
136
137            label = new Label(optionsComposite, SWT.LEFT);
138            label.setFont(curFont);
139            label.setText("");
140        }
141
142        createDatatypeWidget();
143
144        if (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot()) {
145            Button h4SdAttrRadioButton = new Button(optionsComposite, SWT.RADIO);
146            h4SdAttrRadioButton.setFont(curFont);
147            h4SdAttrRadioButton.setText("SD");
148            h4SdAttrRadioButton.setSelection(true);
149
150            h4GrAttrRadioButton = new Button(optionsComposite, SWT.RADIO);
151            h4GrAttrRadioButton.setFont(curFont);
152            h4GrAttrRadioButton.setText("GR");
153        }
154
155        arrayLengthLabel = new Label(content, SWT.LEFT);
156        arrayLengthLabel.setFont(curFont);
157        arrayLengthLabel.setText("Array Size: ");
158
159        lengthField = new Text(content, SWT.SINGLE | SWT.BORDER);
160        lengthField.setFont(curFont);
161        lengthField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
162        lengthField.setTextLimit(30);
163        lengthField.setText("1");
164
165        label = new Label(content, SWT.LEFT);
166        label.setFont(curFont);
167        label.setText("Value: ");
168
169        valueField = new Text(content, SWT.SINGLE | SWT.BORDER);
170        valueField.setFont(curFont);
171        valueField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
172        valueField.setText("0");
173
174        label = new Label(content, SWT.LEFT);
175        label.setFont(curFont);
176        label.setText("Object List: ");
177
178        objChoice = new Combo(content, SWT.DROP_DOWN | SWT.READ_ONLY);
179        objChoice.setFont(curFont);
180        objChoice.setEnabled(true);
181        objChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
182        objChoice.addSelectionListener(new SelectionAdapter() {
183            @Override
184            public void widgetSelected(SelectionEvent e) {
185                String objName = objChoice.getItem(objChoice.getSelectionIndex());
186
187                long ref = -1;
188                try {
189                    HObject obj = fileFormat.get(objName);
190                    ref = obj.getOID()[0];
191                }
192                catch (Exception ex) {
193                    log.debug("object id:", ex);
194                }
195
196                if (ref > 0) {
197                    if (valueField.getText().length() > 1) {
198                        valueField.setText(valueField.getText() + "," + ref);
199                        StringTokenizer st = new StringTokenizer(valueField.getText(), ",");
200                        lengthField.setText(String.valueOf(st.countTokens()));
201                    }
202                    else {
203                        valueField.setText(String.valueOf(ref));
204                        lengthField.setText("1");
205                    }
206                }
207            }
208        });
209
210        Iterator<?> it = objList.iterator();
211        HObject hobj;
212        while (it.hasNext()) {
213            hobj = (HObject) it.next();
214
215            if (hobj instanceof Group) {
216                if (((Group) hobj).isRoot()) continue;
217            }
218
219            objChoice.add(hobj.getFullName());
220        }
221
222        // Add label to take up extra space when resizing dialog
223        label = new Label(content, SWT.LEFT);
224        label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
225
226        // Create Ok/Cancel/Help button region
227        Composite buttonComposite = new Composite(shell, SWT.NONE);
228        buttonComposite.setLayout(new GridLayout(3, false));
229        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
230
231        Button okButton = new Button(buttonComposite, SWT.PUSH);
232        okButton.setFont(curFont);
233        okButton.setText("   &OK   ");
234        okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
235        okButton.addSelectionListener(new SelectionAdapter() {
236            @Override
237            public void widgetSelected(SelectionEvent e) {
238                if (createAttribute()) {
239                    shell.dispose();
240                }
241            }
242        });
243
244        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
245        cancelButton.setFont(curFont);
246        cancelButton.setText(" &Cancel ");
247        cancelButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
248        cancelButton.addSelectionListener(new SelectionAdapter() {
249            @Override
250            public void widgetSelected(SelectionEvent e) {
251                newObject = null;
252                shell.dispose();
253            }
254        });
255
256        Button helpButton = new Button(buttonComposite, SWT.PUSH);
257        helpButton.setFont(curFont);
258        helpButton.setText(" &Help ");
259        helpButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
260        helpButton.addSelectionListener(new SelectionAdapter() {
261            @Override
262            public void widgetSelected(SelectionEvent e) {
263                new HelpDialog(shell).open();
264            }
265        });
266
267        shell.pack();
268
269        shell.addDisposeListener(new DisposeListener() {
270            @Override
271            public void widgetDisposed(DisposeEvent e) {
272                if (curFont != null) curFont.dispose();
273            }
274        });
275
276        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
277
278        Rectangle parentBounds = parent.getBounds();
279        Point shellSize = shell.getSize();
280        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
281                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
282
283        shell.open();
284
285        Display display = shell.getDisplay();
286        while (!shell.isDisposed())
287            if (!display.readAndDispatch())
288                display.sleep();
289    }
290
291    @SuppressWarnings("unchecked")
292    private boolean createAttribute() {
293        Object value = null;
294        String strValue = valueField.getText();
295
296        String attrName = nameField.getText();
297        if (attrName != null) {
298            attrName = attrName.trim();
299        }
300
301        if ((attrName == null) || (attrName.length() < 1)) {
302            Tools.showError(shell, "Create", "No attribute name specified.");
303            return false;
304        }
305
306        String lengthStr = lengthField.getText();
307        log.trace("Name is {} : Length={} and Value={}", attrName, lengthStr, strValue);
308
309        int arraySize = 0;
310        if ((lengthStr == null) || (lengthStr.length() <= 0)) {
311            arraySize = 1;
312        }
313        else {
314            try {
315                arraySize = Integer.parseInt(lengthStr);
316            }
317            catch (Exception e) {
318                arraySize = -1;
319            }
320        }
321
322        if (arraySize <= 0) {
323            Tools.showError(shell, "Create", "Invalid attribute length.");
324            return false;
325        }
326
327        StringTokenizer st = new StringTokenizer(strValue, ",");
328        int count = Math.min(arraySize, st.countTokens());
329        String theToken;
330        log.trace("Count of Values is {}", count);
331
332        // set datatype class
333        Datatype datatype = super.createNewDatatype(null);
334        if (isVLen) {
335            log.trace("Attribute isVLen={} and tsize={}", isVLen, tsize);
336            String[] strArray = { strValue };
337            value = strArray;
338        }
339        else {
340            if (tclass == Datatype.CLASS_STRING) {
341                if (!isVlenStr) {
342                    if (strValue.length() > tsize) {
343                        strValue = strValue.substring(0, tsize);
344                    }
345                }
346
347                String[] strArray = { strValue };
348                value = strArray;
349
350                if (isH5) {
351                    arraySize = 1; // support string type
352                }
353                else {
354                    arraySize = tsize; // array of characters
355                }
356                log.trace("Attribute CLASS_STRING: isVLen={} and tsize={} and arraySize={}", isVLen, tsize, arraySize);
357            }
358            else if (tclass == Datatype.CLASS_REFERENCE) {
359                arraySize = st.countTokens();
360                long[] ref = new long[arraySize];
361                for (int j = 0; j < arraySize; j++) {
362                    theToken = st.nextToken().trim();
363                    try {
364                        ref[j] = Long.parseLong(theToken);
365                    }
366                    catch (NumberFormatException ex) {
367                        Tools.showError(shell, "Create", ex.getMessage());
368                        return false;
369                    }
370                }
371
372                value = ref;
373                log.trace("Attribute CLASS_REFERENCE: tsize={} and arraySize={}", tsize, arraySize);
374            }
375            else if (tclass == Datatype.CLASS_INTEGER) {
376                if (tsign == Datatype.SIGN_NONE) {
377                    if (tsize == 1) {
378                        byte[] b = new byte[arraySize];
379                        short sv = 0;
380                        for (int j = 0; j < count; j++) {
381                            theToken = st.nextToken().trim();
382                            try {
383                                sv = Short.parseShort(theToken);
384                            }
385                            catch (NumberFormatException ex) {
386                                Tools.showError(shell, "Create", ex.getMessage());
387                                return false;
388                            }
389                            if (sv < 0) {
390                                sv = 0;
391                            }
392                            else if (sv > 255) {
393                                sv = 255;
394                            }
395                            b[j] = (byte) sv;
396                        }
397                        value = b;
398                    }
399                    else if (tsize == 2) {
400                        short[] s = new short[arraySize];
401                        int iv = 0;
402                        for (int j = 0; j < count; j++) {
403                            theToken = st.nextToken().trim();
404                            try {
405                                iv = Integer.parseInt(theToken);
406                            }
407                            catch (NumberFormatException ex) {
408                                Tools.showError(shell, "Create", ex.getMessage());
409                                return false;
410                            }
411                            if (iv < 0) {
412                                iv = 0;
413                            }
414                            else if (iv > 65535) {
415                                iv = 65535;
416                            }
417                            s[j] = (short) iv;
418                        }
419                        value = s;
420                    }
421                    else if (tsize == 4) {
422                        int[] i = new int[arraySize];
423                        long lv = 0;
424                        for (int j = 0; j < count; j++) {
425                            theToken = st.nextToken().trim();
426                            try {
427                                lv = Long.parseLong(theToken);
428                            }
429                            catch (NumberFormatException ex) {
430                                Tools.showError(shell, "Create", ex.getMessage());
431                                return false;
432                            }
433                            if (lv < 0) {
434                                lv = 0;
435                            }
436                            if (lv > 4294967295L) {
437                                lv = 4294967295L;
438                            }
439                            i[j] = (int) lv;
440                        }
441                        value = i;
442                    }
443                    else if (tsize == 8) {
444                        long[] i = new long[arraySize];
445                        BigInteger lv = BigInteger.valueOf(0);
446                        for (int j = 0; j < count; j++) {
447                            theToken = st.nextToken().trim();
448                            try {
449                                lv = new BigInteger(theToken);
450                            }
451                            catch (NumberFormatException ex) {
452                                Tools.showError(shell, "Create", ex.getMessage());
453                                return false;
454                            }
455                            i[j] = lv.longValue();
456                        }
457                        value = i;
458                    }
459                }
460                else {
461                    if (tsize == 1) {
462                        byte[] b = new byte[arraySize];
463                        for (int j = 0; j < count; j++) {
464                            theToken = st.nextToken().trim();
465                            try {
466                                b[j] = Byte.parseByte(theToken);
467                            }
468                            catch (NumberFormatException ex) {
469                                Tools.showError(shell, "Create", ex.getMessage());
470                                return false;
471                            }
472                        }
473                        value = b;
474                    }
475                    else if (tsize == 2) {
476                        short[] s = new short[arraySize];
477
478                        for (int j = 0; j < count; j++) {
479                            theToken = st.nextToken().trim();
480                            try {
481                                s[j] = Short.parseShort(theToken);
482                            }
483                            catch (NumberFormatException ex) {
484                                Tools.showError(shell, "Create", ex.getMessage());
485                                return false;
486                            }
487                        }
488                        value = s;
489                    }
490                    else if (tsize == 4) {
491                        int[] i = new int[arraySize];
492
493                        for (int j = 0; j < count; j++) {
494                            theToken = st.nextToken().trim();
495                            try {
496                                i[j] = Integer.parseInt(theToken);
497                            }
498                            catch (NumberFormatException ex) {
499                                Tools.showError(shell, "Create", ex.getMessage());
500                                return false;
501                            }
502                        }
503                        value = i;
504                    }
505                    else if (tsize == 8) {
506                        long[] l = new long[arraySize];
507                        for (int j = 0; j < count; j++) {
508                            theToken = st.nextToken().trim();
509                            try {
510                                l[j] = Long.parseLong(theToken);
511                            }
512                            catch (NumberFormatException ex) {
513                                Tools.showError(shell, "Create", ex.getMessage());
514                                return false;
515                            }
516                        }
517                        value = l;
518                    }
519                }
520            }
521
522            if (tclass == Datatype.CLASS_FLOAT) {
523                if (tsize == 4) {
524                    float[] f = new float[arraySize];
525                    for (int j = 0; j < count; j++) {
526                        theToken = st.nextToken().trim();
527                        try {
528                            f[j] = Float.parseFloat(theToken);
529                        }
530                        catch (NumberFormatException ex) {
531                            Tools.showError(shell, "Create", ex.getMessage());
532                            return false;
533                        }
534                        if (Float.isInfinite(f[j]) || Float.isNaN(f[j])) {
535                            f[j] = 0;
536                        }
537                    }
538                    value = f;
539                }
540                else if (tsize == 8) {
541                    double[] d = new double[arraySize];
542                    for (int j = 0; j < count; j++) {
543                        theToken = st.nextToken().trim();
544                        try {
545                            d[j] = Double.parseDouble(theToken);
546                        }
547                        catch (NumberFormatException ex) {
548                            Tools.showError(shell, "Create", ex.getMessage());
549                            return false;
550                        }
551                        if (Double.isInfinite(d[j]) || Double.isNaN(d[j])) {
552                            d[j] = 0;
553                        }
554                    }
555                    value = d;
556                }
557            }
558        }
559
560        long[] dims = { arraySize };
561        Attribute attr = new Attribute(parentObj, attrName, datatype, dims);
562        attr.setData(value);
563
564        try {
565            parentObj.getFileFormat().writeAttribute(parentObj, attr, false);
566            if (!isH5 && (parentObj instanceof Group) && ((Group) parentObj).isRoot() && h4GrAttrRadioButton.getSelection()) {
567                // don't find a good way to write HDF4 global
568                // attribute. Use the isExisted to separate the
569                // global attribute is GR or SD
570
571                if (((MetaDataContainer) parentObj).getMetadata() == null) {
572                    ((MetaDataContainer) parentObj).getMetadata().add(attr);
573                }
574            }
575            else {
576                log.trace("writeMetadata()");
577                attr.write();
578            }
579        }
580        catch (Exception ex) {
581            Tools.showError(shell, "Create", ex.getMessage());
582            log.debug("createAttribute(): ", ex);
583            return false;
584        }
585
586        newObject = attr;
587
588        return true;
589    }
590
591    private class HelpDialog extends Dialog {
592        private Shell helpShell;
593
594        public HelpDialog(Shell parent) {
595            super(parent, SWT.APPLICATION_MODAL);
596        }
597
598        public void open() {
599            Shell parent = getParent();
600            helpShell = new Shell(parent, SWT.TITLE | SWT.CLOSE |
601                    SWT.RESIZE | SWT.BORDER | SWT.APPLICATION_MODAL);
602            helpShell.setFont(curFont);
603            helpShell.setText("Create New Attribute");
604            helpShell.setImage(ViewProperties.getHdfIcon());
605            helpShell.setLayout(new GridLayout(1, true));
606
607            // Try to create a Browser on platforms that support it
608            try {
609                Browser browser = new Browser(helpShell, SWT.NONE);
610                browser.setFont(curFont);
611                browser.setBounds(0, 0, 500, 500);
612                browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
613
614                if (ClassLoader.getSystemResource("hdf/view/HDFView.class").toString().startsWith("jar")) {
615                    // Attempt to load HTML help file from jar
616                    try (InputStream in = getClass().getClassLoader().getResourceAsStream("hdf/view/NewAttrHelp.html")) {
617                        Scanner scan = new Scanner(in);
618                        StringBuilder buffer = new StringBuilder();
619                        while(scan.hasNextLine()) {
620                            buffer.append(scan.nextLine());
621                        }
622
623                        browser.setText(buffer.toString());
624
625                        scan.close();
626                    }
627                    catch (Exception e) {
628                        StringBuilder buff = new StringBuilder();
629                        buff.append("<html>")
630                            .append("<body>")
631                            .append("ERROR: cannot load help information.")
632                            .append("</body>")
633                            .append("</html>");
634                        browser.setText(buff.toString(), true);
635                    }
636                }
637                else {
638                    try {
639                        URL url = null, url2 = null, url3 = null;
640                        String rootPath = ViewProperties.getViewRoot();
641
642                        try {
643                            url = new URL("file://" + rootPath + "/HDFView.jar");
644                        }
645                        catch (java.net.MalformedURLException mfu) {
646                            log.debug("help information:", mfu);
647                        }
648
649                        try {
650                            url2 = new URL("file://" + rootPath + "/");
651                        }
652                        catch (java.net.MalformedURLException mfu) {
653                            log.debug("help information:", mfu);
654                        }
655
656                        try {
657                            url3 = new URL("file://" + rootPath + "/src/");
658                        }
659                        catch (java.net.MalformedURLException mfu) {
660                            log.debug("help information:", mfu);
661                        }
662
663                        URL uu[] = { url, url2, url3 };
664                        try (URLClassLoader cl = new URLClassLoader(uu)) {
665                            URL u = cl.findResource("hdf/view/NewAttrHelp.html");
666
667                            browser.setUrl(u.toString());
668                        }
669                        catch (Exception ex) {
670                            log.trace("URLClassLoader failed:", ex);
671                        }
672                    }
673                    catch (Exception e) {
674                        StringBuilder buff = new StringBuilder();
675                        buff.append("<html>")
676                            .append("<body>")
677                            .append("ERROR: cannot load help information.")
678                            .append("</body>")
679                            .append("</html>");
680                        browser.setText(buff.toString(), true);
681                    }
682                }
683
684                Button okButton = new Button(helpShell, SWT.PUSH);
685                okButton.setFont(curFont);
686                okButton.setText("   &OK   ");
687                okButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false));
688                okButton.addSelectionListener(new SelectionAdapter() {
689                    @Override
690                    public void widgetSelected(SelectionEvent e) {
691                        helpShell.dispose();
692                    }
693                });
694
695                helpShell.pack();
696
697                helpShell.setSize(new Point(500, 500));
698
699                Rectangle parentBounds = parent.getBounds();
700                Point shellSize = helpShell.getSize();
701                helpShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
702                                      (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
703
704                helpShell.open();
705
706                Display display = parent.getDisplay();
707                while(!helpShell.isDisposed()) {
708                    if (!display.readAndDispatch())
709                        display.sleep();
710                }
711            }
712            catch (Error er) {
713                // Try opening help link in external browser if platform
714                // doesn't support SWT browser
715                Tools.showError(shell, "Browser support",
716                        "Platform doesn't support Browser. Opening external link in web browser...");
717
718                //TODO: Add support for launching in external browser
719            }
720            catch (Exception ex) {
721                log.debug("Open New Attribute Help failure: ", ex);
722            }
723        }
724    }
725
726    /** @return the new attribute created. */
727    public Attribute getAttribute() {
728        return (Attribute)newObject;
729    }
730}