summaryrefslogtreecommitdiff
path: root/source/com/c2kernel/gui/tabs/outcome/form/OutcomeStructure.java
blob: 9333bf8bf8f9667495966f29a4ed0885881905a6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
package com.c2kernel.gui.tabs.outcome.form;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

import org.exolab.castor.types.AnyNode;
import org.exolab.castor.xml.schema.Annotated;
import org.exolab.castor.xml.schema.Annotation;
import org.exolab.castor.xml.schema.ComplexType;
import org.exolab.castor.xml.schema.ContentModelGroup;
import org.exolab.castor.xml.schema.Documentation;
import org.exolab.castor.xml.schema.ElementDecl;
import org.exolab.castor.xml.schema.Group;
import org.exolab.castor.xml.schema.ModelGroup;
import org.exolab.castor.xml.schema.Order;
import org.exolab.castor.xml.schema.Particle;
import org.exolab.castor.xml.schema.SimpleType;
import org.exolab.castor.xml.schema.SimpleTypesFactory;
import org.exolab.castor.xml.schema.XMLType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.c2kernel.gui.tabs.outcome.OutcomeException;
import com.c2kernel.utils.Language;
import com.c2kernel.utils.Logger;

// contains child outcome elements - creates new ones
public abstract class OutcomeStructure extends JPanel {

    ElementDecl model;
    Element myElement = null;
    boolean readOnly;
    HashMap subStructure = new HashMap();
    ArrayList order = new ArrayList();
    String help = "<i>"+Language.translate("No help is available for this element")+"</i>";
    HelpPane helpPane;
    boolean deferChild = false;

    public OutcomeStructure(ElementDecl model, boolean readOnly , HelpPane helpPane) {
        this.model = model;
        this.readOnly = readOnly;
        this.helpPane = helpPane;
        subStructure = new HashMap();
        Logger.msg(8, "Creating " + model.getName() + " structure as " +
            this.getClass().getName().substring(this.getClass().getName().lastIndexOf('.') + 1));
        
        String doc = extractHelp(model);
		if (doc.length() > 0) help = doc;
    }
 
    public boolean getReadOnly() {
        return readOnly;
    }
    /** Contains the rules for deciding which OutcomeStructure will represent a chosen Element Declaration.
     * In this order
     * <ol>
     * <li>if maxOccurs>1 then Dimension
     * <li> SimpleTypes are Fields
     * <li> No element children is a Field
     * <li> Everything else is a DataRecord
     * </ol>
     */
    public OutcomeStructure createStructure(ElementDecl model, boolean readOnly, HelpPane help) throws OutcomeException {
        XMLType elementType = model.getType();
        ComplexType elementComplexType;
        
        if (model.getMaxOccurs() == 0) return null;
        
        // if more than one can occur - dimension
        if (model.getMaxOccurs() > 1 
                || model.getMaxOccurs() == Particle.UNBOUNDED 
                || model.getMinOccurs() == 0) 
            return new Dimension(model, readOnly, help);
        
        // must have a type from now on
        if (elementType == null)
            throw new StructuralException("Element "+model.getName()+" is elementary yet has no type.");
        // simple types will be fields
        if (elementType instanceof SimpleType) return new Field(model, readOnly, help);
        
        // otherwise is a complex type
        try {
            elementComplexType = (ComplexType)elementType;
        }
        catch (ClassCastException e) {
            throw new StructuralException("Unknown XMLType for element " + model.getName());
        }
        
        //when no element children -  field
        if (elementComplexType.getParticleCount() == 0) return new Field(model, readOnly, help);
        
        //everything else is a data record
        return new DataRecord(model, readOnly, help, deferChild);
    }

    /** Extracts child Element declarations from a content group and recursively from any group
     * (not Element) of that group. calls createStructure() to find the corresponding OutcomeStructure
     * then adds it to this structure.
     */
    public void enumerateElements(ContentModelGroup group) throws OutcomeException {

        // process base types first if complex type
        //HACK: castor does not include elements from basetype, so we do it manually. if they fix it, this will duplicate child elements.
        if (group instanceof ComplexType) {
            XMLType base = ((ComplexType)group).getBaseType();
            if (base instanceof ComplexType)
                enumerateElements((ComplexType)base);
            }

        for (Enumeration elements = group.enumerate(); elements.hasMoreElements(); ) {
            Particle thisParticle = (Particle)elements.nextElement();
            if (thisParticle instanceof Group) {
                Group thisGroup = (Group)thisParticle;
                if (thisGroup instanceof ModelGroup) { 
                	// HACK: Castor strangeness - model groups don't seem to resolve their own references. If fixed, this will still work
                    ModelGroup thisModel = (ModelGroup)thisGroup;
                    if (thisModel.hasReference()) thisGroup = thisModel.getReference();
                }
                Order thisOrder = thisGroup.getOrder();
                if (thisOrder == Order.sequence || thisOrder == Order.all) enumerateElements(thisGroup);
                else // we only support sequences in data structures such as these
                    throw new StructuralException("The '"+thisGroup.getOrder()+"' group is not supported");
            }
            else if (thisParticle instanceof ElementDecl) {
                ElementDecl thisElement = (ElementDecl)thisParticle;
                addStructure(createStructure(thisElement, readOnly, helpPane));
            }
            else throw new StructuralException("Particle " + thisParticle.getClass() + " not implemented");
        }
    }

    /** Adds a generated OutcomeStructure as a child of this one. A separate structure as is often overridden.
     */
    public void addStructure(OutcomeStructure newElement) throws OutcomeException {
        if (newElement == null) return;
        subStructure.put(newElement.getName(), newElement);
        order.add(newElement.getName());
    }

    /** After schema processing, addInstance() propogates the XML instance document down the layout.
     * Most OutcomeStructures will throw an exception if called more than once, except Dimension, which is the only
     * Outcome Structure to support maxOccurs>1
     */
    public abstract void addInstance(Element myElement, Document parentDoc) throws OutcomeException;

    public Element getElement() {
        return myElement;
    }
    
    public String getName() {
    	if (model == null) return null;
        return model.getName();
    }
    
    public ElementDecl getModel() {
        return model;
    }
    
    public String getHelp() {
        return help;
    }
    
    public String validateStructure() {
        StringBuffer errors = new StringBuffer();
        for (Iterator iter = subStructure.values().iterator(); iter.hasNext();) {
            OutcomeStructure element = (OutcomeStructure)iter.next();
            errors.append(element.validateStructure());
        }
        return errors.toString();
    }
   
    public abstract Element initNew(Document parent);
    
    public static String extractHelp(Annotated model) {
        Enumeration e = model.getAnnotations();
        StringBuffer doc = new StringBuffer();
        if (e.hasMoreElements()) { // look for HTML
            Annotation note = (Annotation)e.nextElement();
            for (Enumeration g = note.getDocumentation(); g.hasMoreElements();) {
                Documentation thisDoc = (Documentation)g.nextElement();
                for (Enumeration h = thisDoc.getObjects(); h.hasMoreElements();) {
                    AnyNode node = (AnyNode)h.nextElement();
                    String line = node.toString();
                    if (line.length() == 0)
                        line = node.getStringValue();
                    if (line.length() > 0) {
                        doc.append(line).append("\n");
                    }
                }
            }            
        }
       
        return doc.toString();
    }
    
    public abstract void grabFocus();

    public static Class getJavaClass(int typeCode) {
        switch (typeCode) {
            
            // boolean
            case SimpleTypesFactory.BOOLEAN_TYPE:
                return Boolean.class;
                
            // integers
            case SimpleTypesFactory.INTEGER_TYPE:
            case SimpleTypesFactory.NON_POSITIVE_INTEGER_TYPE:
            case SimpleTypesFactory.NEGATIVE_INTEGER_TYPE:
            case SimpleTypesFactory.NON_NEGATIVE_INTEGER_TYPE:
            case SimpleTypesFactory.POSITIVE_INTEGER_TYPE:
            case SimpleTypesFactory.INT_TYPE:
            case SimpleTypesFactory.UNSIGNED_INT_TYPE:
            case SimpleTypesFactory.SHORT_TYPE:        
            case SimpleTypesFactory.UNSIGNED_SHORT_TYPE:
            case SimpleTypesFactory.LONG_TYPE:
            case SimpleTypesFactory.UNSIGNED_LONG_TYPE:
            case SimpleTypesFactory.BYTE_TYPE:
            case SimpleTypesFactory.UNSIGNED_BYTE_TYPE:
                return BigInteger.class;
            // floats
            case SimpleTypesFactory.FLOAT_TYPE:
            case SimpleTypesFactory.DOUBLE_TYPE:
            case SimpleTypesFactory.DECIMAL_TYPE:
                return BigDecimal.class;
            
            // images
            case SimpleTypesFactory.BASE64BINARY_TYPE:
            case SimpleTypesFactory.HEXBINARY_TYPE:
             return ImageIcon.class;
             
            // everything else is a string for now                       
            default:
                return String.class;
        }
    }
    
    public static Object getTypedValue(String value, Class type) {
        try {
            if (type.equals(Boolean.class))
                if (value == null || value.equals(""))
                    return Boolean.FALSE;
                else
                    return Boolean.valueOf(value);
            else if (type.equals(BigInteger.class))
                if (value == null || value.equals(""))
                    return new BigInteger("0");
                else    
                    return new BigInteger(value);
            else if (type.equals(BigDecimal.class))
                if (value == null || value.equals(""))
                    return new BigDecimal(0);
                else
                    return new BigDecimal(value);
        } catch (Exception ex) {
            Logger.error("Cannot convert value '"+value+"' to a "+type.getName());
        }
        return value==null?"":value;
    }
    
    public static boolean isEmpty(Object value) {
        if (value == null) return true;
                
        if (value instanceof String) {
            if (((String)value).length() == 0) return true;
        }
        else if (value instanceof Boolean) {
            if (((Boolean)value).booleanValue() == false) return true;
        }
        else if (value instanceof BigInteger) {
            if (((BigInteger)value).intValue() == 0) return true;
        }
        else if (value instanceof BigDecimal) {
            if (((BigDecimal)value).floatValue() == 0.0) return true;
        }
        return false;
    }
}