View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2005 Kevin Rogers
4   //
5   // This file is part of MillScript.
6   //
7   // MillScript is free software; you can redistribute it and/or modify it under
8   // the terms of the GNU General Public License as published by the Free
9   // Software Foundation; either version 2 of the License, or (at your option)
10  // any later version.
11  //
12  // MillScript is distributed in the hope that it will be useful, but WITHOUT
13  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
15  // more details.
16  //
17  // You should have received a copy of the GNU General Public License along with
18  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
19  // Place, Suite 330, Boston, MA  02111-1307  USA
20  ////////////////////////////////////////////////////////////////////////////////
21  package org.millscript.commons.alert;
22  
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.LinkedList;
26  import java.util.Set;
27  
28  /**
29   * This class represents an alert and is used in combination with a normal
30   * exception to indicate a abnormal condition in the system.
31   */
32  public class Alert extends RuntimeException {
33  
34      /**
35       * This is the ID from the first release for future compatibility.
36       */
37      private static final long serialVersionUID = 3258416140119128372L;
38  
39      /**
40       * This alerts complaint.
41       */
42      private final String complaint;
43  
44      /**
45       * List of this alerts culprits.
46       */
47      private final LinkedList<Culprit> culprits = new LinkedList<Culprit>();
48  
49      /**
50       * The decorators which have decorated this alert are logged here.
51       */
52      private Set<Object> decorators = new HashSet<Object>();
53  
54      /**
55       * Indicates if this alert has had origin information set.
56       */
57      private boolean hasOrigin = false;
58  
59      /**
60       * Indicates if this alert is a mishap. Once this flag has been set to
61       * <code>true</code> it should not be set to <code>false</code>, i.e. once
62       * a mishap - always a mishap.
63       * <p>
64       * TODO - In the future this should be replaced by the traffic light system
65       * Steve was thinking about...
66       * </p>
67       */
68      private boolean isMishap = false;
69  
70      /**
71       * The underlying alert that caused this alert.
72       */
73      private Alert parentAlert = null;
74  
75      /**
76       * The underlying exception that caused this alert.
77       */
78      private Throwable parentThrowable = null;
79  
80      /**
81       * The phase the alert occured during.
82       */
83      private Phase phase = null;
84  
85      /**
86       * The reason for the complaint.
87       */
88      private String reason = null;
89  
90      /**
91       * Constructs a new alert with the specified complaint.
92       *
93       * @param comp  the complaint
94       */
95      public Alert( final String comp ) {
96          super( comp );
97          this.complaint = comp;
98      }
99  
100     /**
101      * Constructs a new alert with the specified complaint and explanation.
102      *
103      * @param comp  the complaint
104      * @param exp   the complaints explanation
105      */
106     public Alert( final String comp, final String exp ) {
107         this( comp );
108         this.reason = exp;
109     }
110 
111     /**
112      * Constructs a new alert with the specified complaint and alert cause.
113      *
114      * @param comp  the complaint
115      * @param pa    the alert that caused this alert to be constructed
116      */
117     public Alert( final String comp, final Alert pa ) {
118         this( comp );
119         this.parentAlert = pa;
120     }
121 
122     /**
123      * Constructs a new alert with the specified complaint and throwable cause.
124      *
125      * @param comp  the complaint
126      * @param pt    the throwable that caused this alert to be constructed
127      */
128     public Alert( final String comp, final Throwable pt ) {
129         this( comp );
130         this.parentThrowable = pt;
131     }
132 
133     /**
134      * Attach a new culprit to this alert, with a boolean as the culprit.
135      *
136      * @param type  the type of culprit
137      * @param value the boolean culprit
138      * @return  a reference to this alert.
139      */
140     public Alert culprit( final String type, final boolean value ) {
141         culprits.addLast( new Culprit( type, Boolean.valueOf( value ) ) );
142         return this;
143     }
144 
145     /**
146      * Attach a new culprit to this alert, with a byte as the culprit.
147      *
148      * @param type  the type of culprit
149      * @param value the byte culprit
150      * @return  a reference to this alert.
151      */
152     public Alert culprit( final String type, final byte value ) {
153         culprits.addLast( new Culprit( type, new Byte( value ) ) );
154         return this;
155     }
156 
157     /**
158      * Attach a new culprit to this alert, with a char as the culprit.
159      *
160      * @param type  the type of culprit
161      * @param value the char culprit
162      * @return  a reference to this alert.
163      */
164     public Alert culprit( final String type, final char value ) {
165         culprits.addLast( new Culprit( type, new Character( value ) ) );
166         return this;
167     }
168 
169     /**
170      * Attach a new culprit to this alert, with a double as the culprit.
171      *
172      * @param type  the type of culprit
173      * @param value the double culprit
174      * @return  a reference to this alert.
175      */
176     public Alert culprit( final String type, final double value ) {
177         culprits.addLast( new Culprit( type, new Double( value ) ) );
178         return this;
179     }
180 
181     /**
182      * Attach a new culprit to this alert, with a float as the culprit.
183      *
184      * @param type  the type of culprit
185      * @param value the float culprit
186      * @return  a reference to this alert.
187      */
188     public Alert culprit( final String type, final float value ) {
189         culprits.addLast( new Culprit( type, new Float( value ) ) );
190         return this;
191     }
192 
193     /**
194      * Attach a new culprit to this alert, with an int as the culprit.
195      *
196      * @param type  the type of culprit
197      * @param value the int culprit
198      * @return  a reference to this alert.
199      */
200     public Alert culprit( final String type, final int value ) {
201         culprits.addLast( new Culprit( type, new Integer( value ) ) );
202         return this;
203     }
204 
205     /**
206      * Attach a new culprit to this alert, with a long as the culprit.
207      *
208      * @param type  the type of culprit
209      * @param value the long culprit
210      * @return  a reference to this alert.
211      */
212     public Alert culprit( final String type, final long value ) {
213         culprits.addLast( new Culprit( type, new Long( value ) ) );
214         return this;
215     }
216 
217     /**
218      * Attach a new culprit to this alert, with a general object as the
219      * culprit.
220      *
221      * @param type  the type of culprit
222      * @param value the object culprit
223      * @return  a reference to this alert.
224      */
225     public Alert culprit( final String type, final Object value ) {
226         culprits.addLast( new Culprit( type, value ) );
227         return this;
228     }
229 
230     /**
231      * Decorates this alert with the information from the supplied decorator. A
232      * given decorator will only decorate an alert once, on the first call to this
233      * method, regardless of how many times you might subsequently try. This
234      * behaviour is to protect you from repeating yourself in the alert detail
235      * message.
236      *
237      * @param decorator the decorator to get information from
238      * @return  this alert after decorating it
239      */
240     public Alert decorate( final AlertDecorator decorator ) {
241         if ( ! decorators.contains( decorator ) ) {
242             decorator.decorate( this );
243             decorators.add( decorator );
244         }
245         return this;
246     }
247 
248     /**
249      * Decorates this alert with information from the supplied object. If the
250      * object is an instance of an {@link AlertDecorator}, it's
251      * {@link AlertDecorator#decorate(Alert)} method will be called otherwise a
252      * generic culprit message will be added to the alert.
253      * <p>
254      * The method allows you to hide implementations of AlertDecorator behind
255      * interfaces and such, so you can use alerts without have to include the
256      * AlertDecorator interface in general API's.
257      * </p>
258      *
259      * @param o the object to decorate this alert with
260      * @return  this alert after decorating it
261      */
262     public Alert decorate( final Object o ) {
263         if ( ! decorators.contains( o ) ) {
264             if ( o instanceof AlertDecorator ) {
265                 ( (AlertDecorator) o ).decorate( this );
266             } else {
267                 this.culprit( "object", o.toString() );
268             }
269             decorators.add( o );
270         }
271         return this;
272     }
273 
274     /**
275      * Reports this alert as an escape, by throwing it wrapped in an escape
276      * exception. This is intended to be used as a by-pass for your normal
277      * alert handling code.
278      *
279      * @return  a new escape exception wrapping this alert
280      */
281     public EscapeException escape() {
282         throw new EscapeException( this );
283     }
284 
285     /**
286      * Returns this alerts complaint.
287      *
288      * @return  a String with this alerts complaint
289      */
290     public String getComplaint() {
291         return complaint;
292     }
293 
294     /**
295      * Returns this alerts culprits.
296      *
297      * @return  a LinkedList of this alerts culprits.
298      */
299     public LinkedList< Culprit > getCulprits() {
300         return culprits;
301     }
302 
303     /**
304      * @see java.lang.Throwable#getMessage()
305      */
306     @Override
307     public String getMessage() {
308         final StringBuffer buffer = new StringBuffer();
309         Iterator it = this.culprits.iterator();
310         while ( it.hasNext() ) {
311             buffer.append( it.next() );
312             buffer.append( '\n' );
313         }
314         return buffer.toString();
315     }
316 
317     /**
318      * Returns the Alert that caused this Alert to be generated.
319      *
320      * @return the Alert that caused this Alert to be generated
321      */
322     public Alert getParentAlert() {
323         return parentAlert;
324     }
325 
326     /**
327      * Returns the Throwable that caused this Alert to be generated.
328      *
329      * @return the Throwable that caused this Alert to be generated
330      */
331     public Throwable getParentThrowable() {
332         return parentThrowable;
333     }
334 
335     /**
336      * Returns the phase this Alert occured in.
337      *
338      * @return the Phase this Alert occured in
339      */
340     public Phase getPhase() {
341         return phase;
342     }
343 
344     /**
345      * Returns the reason for this Alerts complaint
346      *
347      * @return the reason for this Alerts complaint
348      */
349     public String getReason() {
350         return reason;
351     }
352 
353     /**
354      * Returns the mishap indicator flag, <code>true</code> if this
355      * {@link Alert} is a mishap and <code>false</code> otherwise.
356      *
357      * @return  <code>true</code> if this {@link Alert} is a mishap and
358      * <code>false</code> otherwise
359      */
360     public boolean isMishap() {
361         return isMishap;
362     }
363 
364     /**
365      * Marks this alert as a mishap and throws itself.
366      *
367      * @return  this {@link Alert}
368      */
369     public Alert mishap() {
370         // Set the mishap flag..
371         this.isMishap = true;
372         // We can't report yet, the application might not be ready, so throw
373         // ourself
374         throw this;
375     }
376 
377     /**
378      * Decorates this alert with origin information from the supplied alert
379      * origin. An alert can only have one set of origin information, so once
380      * set it cannot be set again.
381      *
382      * @param origin    the decorator to get information from
383      * @return  this alert after decorating it
384      */
385     public Alert origin( final AlertOrigin origin ) {
386         if ( ! this.hasOrigin ) {
387             if ( origin.getOrigin() != null ) {
388                 this.culprit( "origin", origin.getOrigin() );
389             }
390             if ( origin.getLineNumber() > 0 ) {
391                 this.culprit( "line no.", new Integer( origin.getLineNumber() ) );
392             }
393             this.hasOrigin = true;
394         }
395         return this;
396     }
397 
398     /**
399      * Reports this alert as a mishap, by throwing it as an exception. This is
400      * intended to be used when this alert needs to be re-thrown after being
401      * caught but not then chained.
402      *
403      * @return an AlertException for this alert
404      */
405     public Alert remishap() {
406         throw this;
407     }
408 
409     /**
410      * Reports this alert to the specified reporter as a mishap.
411      *
412      * @param reporter  the AlertReporter to report this mishap Alert to
413      */
414     public void report( final AlertReporter reporter ) {
415         reporter.report( this );
416     }
417 
418     /**
419      * Sets the parent alert for this alert.
420      *
421      * @param alert the parent alert
422      * @return  this alert
423      */
424     public Alert setParentAlert( final Alert alert ) {
425         this.parentAlert = alert;
426         return this;
427     }
428 
429     /**
430      * Sets the parent throwable for this alert.
431      *
432      * @param throwable the throwable that caused this alert
433      * @return  this alert
434      */
435     public Alert setParentThrowable( final Throwable throwable ) {
436         this.parentThrowable = throwable;
437         return this;
438     }
439 
440     /**
441      * Sets the phase this alert occured in.
442      *
443      * @param p the phase for this alert
444      */
445     public Alert setPhase( final Phase p ) {
446         if ( this.phase == null ) {
447             this.phase = p;
448         } else {
449 //            new Alert(
450 //                "Phase has already been set"
451 //            ).culprit(
452 //                "existing phase",
453 //                this.phase
454 //            ).culprit(
455 //                "new phase",
456 //                p
457 //            ).warning();
458         }
459         return this;
460     }
461 
462     /**
463      * @see java.lang.Object#toString()
464      */
465     @Override
466     public String toString() {
467         final StringBuffer buffer = new StringBuffer();
468         buffer.append( this.getMessage() );
469         buffer.append( this.getClass().getName() );
470         return buffer.toString();
471     }
472 
473 }