001    /**
002     * <copyright>
003     *
004     * Copyright (c) 2004-2007 IBM Corporation and others.
005     * All rights reserved.   This program and the accompanying materials
006     * are made available under the terms of the Eclipse Public License v1.0
007     * which accompanies this distribution, and is available at
008     * http://www.eclipse.org/legal/epl-v10.html
009     *
010     * Contributors:
011     *   IBM - Initial API and implementation
012     *
013     * </copyright>
014     *
015     * $Id: BasicDiagnostic.java,v 1.15 2009/04/18 11:45:26 emerks Exp $
016     */
017    package org.eclipse.emf.common.util;
018    
019    import java.util.ArrayList;
020    import java.util.Collections;
021    import java.util.List;
022    
023    import org.eclipse.core.runtime.IStatus;
024    
025    import org.eclipse.emf.common.EMFPlugin;
026    
027    
028    /**
029     * A basic implementation of a diagnostic that that also acts as a chain.
030     */
031    public class BasicDiagnostic implements Diagnostic, DiagnosticChain
032    {
033      /**
034       * The severity.
035       * @see #getSeverity
036       */
037      protected int severity;
038    
039      /**
040       * The message.
041       * @see #getMessage
042       */
043      protected String message;
044    
045      /**
046       * The message.
047       * @see #getMessage
048       */
049      protected List<Diagnostic> children;
050    
051      /**
052       * The data.
053       * @see #getData
054       */
055      protected List<?> data;
056    
057      /**
058       * The source.
059       * @see #getSource
060       */
061      protected String source;
062    
063      /**
064       * The code.
065       * @see #getCode
066       */
067      protected int code;
068      
069      /**
070       * Default Constructor (no initialization for local parameters)
071       */
072      public BasicDiagnostic()
073      {
074        super();
075      }
076    
077      public BasicDiagnostic(String source, int code, String message, Object[] data) 
078      {
079        this.source = source;
080        this.code = code;
081        this.message = message;
082        this.data = dataAsList(data);
083      }
084    
085      public BasicDiagnostic(int severity, String source, int code, String message, Object[] data)
086      {
087        this(source, code, message, data);
088        this.severity = severity;
089      }
090    
091      public BasicDiagnostic(String source, int code, List<? extends Diagnostic> children, String message, Object[] data)
092      {
093        this(source, code, message, data);
094        if (children != null)
095        {
096          for (Diagnostic diagnostic : children)
097          {
098            add(diagnostic);
099          }
100        }
101      }
102    
103      protected List<?> dataAsList(Object [] data)
104      {
105        if (data == null)
106        {
107          return Collections.EMPTY_LIST;
108        }
109        else
110        {
111          Object [] copy = new Object [data.length];
112          System.arraycopy(data, 0, copy, 0, data.length);
113          return new BasicEList.UnmodifiableEList<Object>(copy.length, copy);
114        }
115      }
116      
117      protected void setSeverity(int severity)
118      {
119        this.severity = severity;
120      }
121    
122      public int getSeverity()
123      {
124        return severity;
125      }
126    
127      public String getMessage()
128      {
129        return message;
130      }
131    
132      public List<?> getData()
133      {
134        return data;
135      }
136    
137      public List<Diagnostic> getChildren()
138      {
139        if (children == null)
140        {
141          return Collections.emptyList();
142        }
143        else
144        {
145          return Collections.unmodifiableList(children);
146        }
147      }
148    
149      protected void setSource(String source)
150      {
151        this.source = source;
152      }
153    
154      public String getSource()
155      {
156        return source;
157      }
158      
159      protected void setCode(int code)
160      {
161        this.code = code;
162      }
163    
164      public int getCode()
165      {
166        return code;
167      }
168    
169      public void add(Diagnostic diagnostic)
170      {
171        if (children == null)
172        {
173          children = new BasicEList<Diagnostic>();
174        }
175    
176        children.add(diagnostic);
177        int childSeverity = diagnostic.getSeverity();
178        if (childSeverity > getSeverity())
179        {
180          severity = childSeverity;
181        }
182      }
183    
184      public void addAll(Diagnostic diagnostic)
185      {
186        for (Diagnostic child : diagnostic.getChildren())
187        {
188          add(child);
189        }
190      }
191    
192      public void merge(Diagnostic diagnostic)
193      {
194        if (diagnostic.getChildren().isEmpty())
195        {
196          add(diagnostic);
197        }
198        else
199        {
200          addAll(diagnostic);
201        }
202      }
203    
204      public int recomputeSeverity()
205      {
206        if (children != null)
207        {
208          severity = OK;
209          for (Diagnostic child : children)
210          {
211            int childSeverity = child instanceof BasicDiagnostic ? ((BasicDiagnostic)child).recomputeSeverity() : child.getSeverity();
212            if (childSeverity > severity)
213            {
214              severity = childSeverity;
215            }
216          }
217        }
218    
219        return severity;
220      }
221      
222      /**
223       * Returns the first throwable object available in the {@link #data} list, 
224       * which is set when this diagnostic is instantiated.
225       */
226      public Throwable getException()
227      {
228        List<?> data = getData();
229        if (data != null)
230        {
231          for (Object datum : data)
232          {
233            if (datum instanceof Throwable)
234            {
235              return (Throwable)datum;
236            }
237          }
238        }
239        return null;
240      }
241      
242      @Override
243      public String toString()
244      {
245        StringBuilder result = new StringBuilder();
246        result.append("Diagnostic ");
247        switch (severity)
248        {
249          case OK: 
250          {
251            result.append("OK"); 
252            break;
253          }
254          case INFO: 
255          {
256            result.append("INFO"); 
257            break;
258          }
259          case WARNING: 
260          {
261            result.append("WARNING"); 
262            break;
263          }
264          case ERROR: 
265          {
266            result.append("ERROR"); 
267            break;
268          }
269          case CANCEL: 
270          {
271            result.append("CANCEL"); 
272            break;
273          }
274          default:
275          {
276            result.append(Integer.toHexString(severity));
277            break;
278          }
279        }
280    
281        result.append(" source="); 
282        result.append(source);
283    
284        result.append(" code="); 
285        result.append(code);
286    
287        result.append(' ');
288        result.append(message);
289    
290        if (data != null)
291        {
292          result.append(" data=");
293          result.append(data);
294        }
295        if (children != null)
296        {
297          result.append(' ');
298          result.append(children);
299        }
300    
301        return result.toString();
302      }  
303    
304      private static class StatusWrapper implements IStatus
305      {
306        protected static final IStatus [] EMPTY_CHILDREN = new IStatus [0];
307    
308        protected Throwable throwable;
309        protected Diagnostic diagnostic;
310        protected IStatus [] wrappedChildren;
311    
312        public StatusWrapper(Diagnostic diagnostic)
313        {
314          this.diagnostic = diagnostic;
315        }
316        
317        public StatusWrapper(DiagnosticException diagnosticException)
318        {
319          throwable = diagnosticException;
320          diagnostic = diagnosticException.getDiagnostic();
321        }
322    
323        public IStatus[] getChildren()
324        {
325          if (wrappedChildren == null)
326          {
327            List<Diagnostic> children = diagnostic.getChildren();
328            if (children.isEmpty())
329            {
330              wrappedChildren = EMPTY_CHILDREN;
331            }
332            else
333            {
334              wrappedChildren = new IStatus [children.size()];
335              for (int i = 0; i < wrappedChildren.length; ++i)
336              {
337                wrappedChildren[i] = toIStatus(children.get(i));
338              }
339            }
340          }
341          return wrappedChildren;
342        }
343        
344        public int getCode()
345        {
346          return diagnostic.getCode();
347        }
348        
349        public Throwable getException()
350        {
351          return throwable != null ? throwable : diagnostic.getException();
352        }
353        
354        public String getMessage()
355        {
356          return diagnostic.getMessage();
357        }
358        
359        public String getPlugin()
360        {
361          return diagnostic.getSource();
362        }
363        
364        public int getSeverity()
365        {
366          return diagnostic.getSeverity();
367        }
368        
369        public boolean isMultiStatus()
370        {
371          return !diagnostic.getChildren().isEmpty();
372        }
373        
374        public boolean isOK()
375        {
376          return diagnostic.getSeverity() == OK;
377        }
378        
379        public boolean matches(int severityMask)
380        {
381          return (diagnostic.getSeverity() & severityMask ) != 0;
382        }
383    
384        @Override
385        public String toString()
386        {
387          return diagnostic.toString();
388        }
389    
390        public static IStatus convert(Diagnostic diagnostic)
391        {
392          return 
393            diagnostic instanceof DiagnosticWrapper ?
394              ((DiagnosticWrapper)diagnostic).status :
395              new StatusWrapper(diagnostic);
396        }
397    
398        public static IStatus create(DiagnosticException diagnosticException)
399        {
400          return new StatusWrapper(diagnosticException);
401        }
402      }
403    
404      /**
405       * Returns the diagnostic viewed as an {@link IStatus}.
406       */
407      public static IStatus toIStatus(Diagnostic diagnostic)
408      {
409        return StatusWrapper.convert(diagnostic);
410      }
411    
412      /**
413       * Returns the diagnostic exception viewed as an {@link IStatus}.
414       */
415      public static IStatus toIStatus(DiagnosticException diagnosticException)
416      {
417        return StatusWrapper.create(diagnosticException);
418      }
419      
420      private static class DiagnosticWrapper implements Diagnostic
421      {
422        protected IStatus status;
423        protected List<Diagnostic> wrappedChildren;
424        protected List<Diagnostic> unmodifiableWrappedChildren;
425        protected List<Object> data;
426    
427        public DiagnosticWrapper(IStatus status)
428        {
429          this.status = status;
430        }
431        
432        public int getCode()
433        {
434          return status.getCode();
435        }
436    
437        public String getMessage()
438        {
439          return status.getMessage();
440        }
441    
442        public int getSeverity()
443        {
444          return status.getSeverity();
445        }
446    
447        public String getSource()
448        {
449          return status.getPlugin();
450        }
451        
452        public Throwable getException()
453        {
454          return status.getException();
455        }
456    
457        public List<Diagnostic> basicGetChildren()
458        {
459          if (wrappedChildren == null)
460          {
461            IStatus[] children = status.getChildren();
462            if (children.length == 0)
463            {
464              wrappedChildren = new ArrayList<Diagnostic>();
465            }
466            else
467            {
468              wrappedChildren = new ArrayList<Diagnostic>(children.length);
469              for (IStatus child : children)
470              {
471                wrappedChildren.add(toDiagnostic(child));
472              }
473            }
474          }
475          return wrappedChildren;      
476        }
477    
478        public List<Diagnostic> getChildren()
479        {
480          if (unmodifiableWrappedChildren == null)
481          {
482            unmodifiableWrappedChildren = Collections.unmodifiableList(basicGetChildren());
483          }
484          return unmodifiableWrappedChildren;
485        }
486    
487        public List<?> getData()
488        {
489          if (data == null)
490          {
491            List<Object> list = new ArrayList<Object>(2);
492            Throwable exception = getException();
493            if (exception != null)
494            {
495              list.add(exception);
496            }
497            list.add(status);
498            data = Collections.unmodifiableList(list);
499          }
500          return data;
501        }
502    
503        public static Diagnostic convert(IStatus status)
504        {
505          return 
506            status instanceof StatusWrapper ?
507              ((StatusWrapper)status).diagnostic :
508              new DiagnosticWrapper(status);
509        }
510      }
511      
512      public static Diagnostic toDiagnostic(IStatus status)
513      {
514        return DiagnosticWrapper.convert(status);
515      }
516    
517      /**
518       * Returns the throwable viewed as a {@link Diagnostic}.
519       * 
520       * @param throwable
521       * @return {@link Diagnostic}
522       */
523      public static Diagnostic toDiagnostic(Throwable throwable)
524      {
525        if (throwable instanceof DiagnosticException)
526        {
527          return ((DiagnosticException)throwable).getDiagnostic();
528        }
529        else if (throwable instanceof WrappedException)
530        {
531          return toDiagnostic(throwable.getCause());
532        }
533        
534        if (EMFPlugin.IS_ECLIPSE_RUNNING)
535        {
536          Diagnostic diagnostic = EclipseHelper.toDiagnostic(throwable);
537          if (diagnostic != null)
538          {
539            return diagnostic;
540          }
541        }
542        
543        String message = throwable.getClass().getName();
544        int index = message.lastIndexOf('.');
545        if (index >= 0)
546        {
547          message = message.substring(index + 1);
548        }
549        if (throwable.getLocalizedMessage() != null)
550        {
551          message = message + ": " + throwable.getLocalizedMessage();
552        }
553        
554        BasicDiagnostic basicDiagnostic = 
555          new BasicDiagnostic
556            (Diagnostic.ERROR,
557             "org.eclipse.emf.common", 
558             0, 
559             message,
560             new Object[] { throwable });
561        
562        if (throwable.getCause() != null && throwable.getCause() != throwable)
563        {
564          throwable = throwable.getCause();
565          basicDiagnostic.add(toDiagnostic(throwable));
566        }
567        
568        return basicDiagnostic;
569      }
570      
571      private static class EclipseHelper
572      {
573        public static Diagnostic toDiagnostic(Throwable throwable)
574        {
575          if (throwable instanceof org.eclipse.core.runtime.CoreException)
576          {
577            IStatus status = ((org.eclipse.core.runtime.CoreException)throwable).getStatus();
578            DiagnosticWrapper wrapperDiagnostic = new DiagnosticWrapper(status);
579            Throwable cause = throwable.getCause();
580            if (cause != null && cause != throwable)
581            {
582              wrapperDiagnostic.basicGetChildren().add(BasicDiagnostic.toDiagnostic(cause));
583            }
584            return wrapperDiagnostic;
585          }
586          return null;
587        }
588      }
589    }