Subversion Repository Public Repository

litesoft

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
284
285
286
287
288
289
package com.temp.foundation.client.support;

import org.litesoft.externalization.shared.*;

import com.temp.common.shared.utils.*;
import com.temp.foundation.client.widget.dialog.*;

import java.util.*;

/**
 * Helper to create the: Title, Body text blocks, & an indicator regarding
 * displaying the Details button for the ExceptionDialog.
 * <p/>
 * The key to its flexibility and effectiveness is the application of the
 * "source", "context", and type of the "throwable" to a template source (in
 * this case the ExternalizedText object). By selecting different template
 * strings and then selectively either disabling different results or injecting
 * values, quite a variety of different looks can be achieved.
 * <p/>
 * The "source" is either from the RPCall's "callerSourceObject" and is the
 * Simple Class Name of this object (e.g. DashboardActivity), or "Uncaught".
 * <p/>
 * The "context" is null if the "source" is "Uncaught", otherwise it is the
 * Method Name from the "MethodWithExpectedExceptions" passed to the RPCall.
 * <p/>
 * The "throwable" is the problem either caught by the
 * "UncaughtExceptionHandler" or the un-handled problem fro mthe RPCall.
 *
 * @author georgs
 * @see E13nSubstitutionData
 */
public class ExceptionDisplayHelper {

    private static final String SUBSTITUTION_ID_NO_PREFIX = E13nResolver.INIT + "No";

    // There are three Detail Button States:
    //
    //    1) {NoDetails} -> No Button
    //    2) {Details} -> I want the Details Button HERE
    //    3) Unspecified -> ExceptionDialog will add it where ever it wants...

    private static final String DEFAULT_SOURCE = E13nResolver.SPACE_SUBSTITUTION_ID + " " + ExceptionDialog.DETAILS_BUTTON_SUBSTITUTION_ID;

    // all the template keys for this class start with:
    private static final String KEY_NAME_PREFIX = "exception";

    // Don't add the Details Button Indicator
    private static final String NO_DETAILS_BUTTON_SUBSTITUTION_ID = SUBSTITUTION_ID_NO_PREFIX + ExceptionDialog.DETAILS + E13nResolver.FINI;

    // Don't add the Additional Text blocks Indicator
    private static final String NO_ADDITIONAL_TEXT_SUBSTITUTION_ID = SUBSTITUTION_ID_NO_PREFIX + "AdditionalText" + E13nResolver.FINI;

    // Don't add the block named Indicator
    private static String noNamed( String name ) {
        return SUBSTITUTION_ID_NO_PREFIX + name + E13nResolver.FINI;
    }

    // Add the text associated with the name Indicator
    private static String named( String name ) {
        return E13nResolver.INIT + name + E13nResolver.FINI;
    }

    private final StringProxy titleText;
    private final StringProxy sourceText;
    private final StringProxy contextText;
    private final StringProxy typeText;
    private final StringProxy messageText;
    private final String[] additionalTextBlocks;
    private boolean noDetails, noAdditionalTextBlocks;

    public ExceptionDisplayHelper( E13nSubstitutionData sd, String source, String context, Throwable throwable, String... additionalTextBlocks ) {
        titleText = new StringProxy( sd, "Title" );
        sourceText = new StringProxy( sd, "Source" );
        contextText = new StringProxy( sd, "Context" );
        typeText = new StringProxy( sd, "Type" );
        messageText = new StringProxy( sd, "Message" );
        this.additionalTextBlocks = additionalTextBlocks;

        source = Assert.noEmpty( "source", source );
        context = StringUtils.noEmpty( context );

        // Build Options such that there is always an empty string on the end:
        String[] sourceOptions = getSourceOptions( source );
        String[] contextOptions = getContextOptions( context );
        String[] typeOptions = getTypeOptions( throwable );

        String type = typeOptions[0];
        String message = throwable.getMessage();

        // 1st set the base line texts from the template source appropriately
        titleText.setTextCombinatorically( sourceOptions, contextOptions, typeOptions, null );
        sourceText.setTextCombinatorically( sourceOptions, contextOptions, typeOptions, DEFAULT_SOURCE );
        contextText.setTextCombinatorically( sourceOptions, contextOptions, typeOptions, null );
        typeText.setTextCombinatorically( sourceOptions, contextOptions, typeOptions, null );
        messageText.setTextCombinatorically( sourceOptions, contextOptions, typeOptions, null );

        // Disable each of the following if the others listed indicate.
        // This facilitates having to put fewer entries in the Template Source by
        // disabling "downstream" fields, e.g.:
        //      exceptionTypeInvalidFilterNameException : "{NoMessage}{DontShow}"           // would disable both the Type and Message display
        //      exceptionSourceDashboardActivity : "{NoContext}{NoType}{NoMessage}...",     // would disable Context, Type, & Message
        contextText.disableIfNoRequestedIn( sourceText, typeText, messageText );
        typeText.disableIfNoRequestedIn( sourceText, contextText, messageText );
        messageText.disableIfNoRequestedIn( sourceText, contextText, typeText );

        // If source indicates, disable the Details button.
        noDetails = sourceText.noDetails();

        // If any indicate, disable the Additional Text Blocks.
        noAdditionalTextBlocks = sourceText.noAdditionalText() | // Note the SINGLE '|'
                                 contextText.noAdditionalText() | //
                                 typeText.noAdditionalText() | //
                                 messageText.noAdditionalText();

        // Now that all the "no" "flags" have been checked and removed, check if all that is left is the No Show request
        contextText.checkNoShow();
        typeText.checkNoShow();
        messageText.checkNoShow();

        // Inject the actual text into self and others listed as desired/requested.
        // This facility has two purposes:
        //      1) Since each section is treated as a "paragraph" only eliminating & injecting can the "paragraphness" be eliminated.
        //      2) If you wanted to include some text, like the Exception Type in-line with some other text, it needs to be injected.
        contextText.injectActualTextInto( context, sourceText, typeText, messageText );
        typeText.injectActualTextInto( type, sourceText, contextText, messageText );
        messageText.injectActualTextInto( message, sourceText, contextText, typeText );

        // Finish each - if No Add indicated anywhere above, then clear the text!
        sourceText.fini();
        contextText.fini();
        typeText.fini();
        messageText.fini();
    }

    public boolean noDetails() {
        return noDetails;
    }

    public String getTitle() {
        return titleText.text;
    }

    public String[] getTextBlocks() {
        List<String> blocks = new ArrayList<String>();
        sourceText.addTo( blocks );
        contextText.addTo( blocks );
        typeText.addTo( blocks );
        messageText.addTo( blocks );
        if ( additionalTextBlocks != null && !noAdditionalTextBlocks ) {
            for ( String block : additionalTextBlocks ) {
                blocks.add( block );
            }
        }
        return blocks.toArray( new String[blocks.size()] );
    }

    private String[] getSourceOptions( String source ) {
        return new String[]{source, ""};
    }

    private String[] getContextOptions( String context ) {
        return (context == null) ? new String[]{""} : new String[]{context, ""};
    }

    private String[] getTypeOptions( Throwable throwable ) {
        Class<?> klass = throwable.getClass();
        List<String> types = new ArrayList<String>();
        do {
            types.add( ObjectUtils.getSimpleName( klass ) );
            klass = klass.getSuperclass();
        } while ( klass != null && klass != Object.class );
        types.add( "" );
        return types.toArray( new String[types.size()] );
    }

    private static class StringProxy {
        private final E13nSubstitutionData sd;
        private final String prefix, namedSubId, noNamedSubId;
        private boolean noAdd = false;
        private String text = null;

        StringProxy( E13nSubstitutionData sd, String name ) {
            this.sd = sd;
            prefix = KEY_NAME_PREFIX + name;
            namedSubId = named( name );
            noNamedSubId = noNamed( name );
        }

        public boolean noDetails() {
            return (-1 != remove( NO_DETAILS_BUTTON_SUBSTITUTION_ID ));
        }

        public boolean noAdditionalText() {
            return (-1 != remove( NO_ADDITIONAL_TEXT_SUBSTITUTION_ID ));
        }

        public void disableIfNoRequestedIn( StringProxy... disablers ) {
            for ( StringProxy them : disablers ) {
                if ( -1 != them.remove( noNamedSubId ) ) {
                    noAdd = true;
                }
            }
        }

        public void checkNoShow() {
            if ( -1 != remove( E13nResolver.DONT_SHOW_SUBSTITUTION_ID ) ) {
                noAdd = true;
                text = null;
            }
        }

        public void injectActualTextInto( String actualText, StringProxy... possibleRequestors ) {
            inject( namedSubId, actualText ); // self
            for ( StringProxy them : possibleRequestors ) {
                them.inject( namedSubId, actualText );
            }
        }

        public void fini() {
            if ( noAdd ) {
                text = null;
            }
        }

        public void addTo( List<String> blocks ) {
            if ( text != null ) {
                blocks.add( text );
            }
        }

        /**
         * Set the 'text' by trying all the Combinations in priority order:
         * <nl>
         * <li>SourceOptions</li>
         * <li>ContextOptions</li>
         * <li>TypeOptions</li>
         * </nl>
         * This results in:
         * <nl>
         * <li>SCTs</li>
         * <li>SC_</li>
         * <li>S_Ts</li>
         * <li>S__</li>
         * <li>_CTs</li>
         * <li>_C_</li>
         * <li>__Ts</li>
         * <li>___</li>
         * </nl>
         *
         * @param sourceOptions  a two element array w/ the source and an empty string.
         * @param contextOptions a one or two element array based on if the context was not
         *                       null, with the last element being an empty String.
         * @param typeOptions    the Throwable Type Hierarchy of Simple Class Names with a
         *                       trailing empty String
         */
        public void setTextCombinatorically( String[] sourceOptions, String[] contextOptions, String[] typeOptions, String defaultValue ) {
            for ( int sourceIndex = 0; (text == null) && (sourceIndex < sourceOptions.length); sourceIndex++ ) {
                String sourcePrefix = prefix + sourceOptions[sourceIndex];
                for ( int contextIndex = 0; (text == null) && (contextIndex < contextOptions.length); contextIndex++ ) {
                    String contextPrefix = sourcePrefix + contextOptions[contextIndex];
                    for ( int typeIndex = 0; (text == null) && (typeIndex < typeOptions.length); typeIndex++ ) {
                        text = sd.get( contextPrefix + typeOptions[typeIndex] );
                    }
                }
            }
            if ( text == null ) {
                text = defaultValue;
            }
        }

        private int remove( String textToFind ) {
            if ( text != null ) {
                int posAt = text.indexOf( textToFind );
                if ( posAt != -1 ) {
                    text = text.substring( 0, posAt ) + text.substring( posAt + textToFind.length() );
                    return posAt;
                }
            }
            return -1;
        }

        private void inject( String namedSubIdToRemove, String actualText ) {
            int posAt = remove( namedSubIdToRemove );
            if ( (-1 != posAt) && (actualText != null) ) {
                text = text.substring( 0, posAt ) + actualText + text.substring( posAt );
            }
        }
    }
}

Commits for litesoft/trunk/GWT_Sandbox/FormEngine/src/com/temp/foundation/client/support/ExceptionDisplayHelper.java

Diff revisions: vs.
Revision Author Commited Message
965 GeorgeS picture GeorgeS Fri 01 Aug, 2014 03:20:35 +0000

!