Coverage Report - eu.simuline.octave.type.cast.Cast
 
Classes in this File Line Coverage Branch Coverage Complexity
Cast
85%
29/34
83%
15/18
4.889
Cast$ClassPair
66%
18/27
40%
9/22
4.889
 
 1  
 /*
 2  
  * Copyright 2009 Ange Optimization ApS
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *     http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package eu.simuline.octave.type.cast;
 17  
 
 18  
 import java.util.HashMap;
 19  
 import java.util.Iterator;
 20  
 import java.util.Map;
 21  
 
 22  
 import javax.imageio.spi.ServiceRegistry;
 23  
 
 24  
 import eu.simuline.octave.exception.OctaveClassCastException;
 25  
 import eu.simuline.octave.exception.OctaveCastServiceException;
 26  
 import eu.simuline.octave.type.OctaveObject;
 27  
 
 28  
 /**
 29  
  * Helper class for the auto cast functionality. 
 30  
  * Currently, only a single caster is implemented: 
 31  
  * {@link DoubleToComplexCaster} but this can be dynamically extended. 
 32  
  */
 33  
 public final class Cast {
 34  
 
 35  
     private static final int PRIME = 31;
 36  
  
 37  
     /**
 38  
      * Maps a class pair to an according caster. 
 39  
      */
 40  
     private static Map<ClassPair<?, ?>, Caster<?, ?>> casterMap;
 41  
 
 42  0
     private Cast() {
 43  0
         throw new UnsupportedOperationException("Do not instantiate");
 44  
     }
 45  
 
 46  
     @SuppressWarnings("unchecked")
 47  
     private static synchronized void initIfNecessary() {
 48  6
         if (casterMap == null) {
 49  2
             casterMap = new HashMap<ClassPair<?, ?>, Caster<?, ?>>();
 50  
             @SuppressWarnings("rawtypes")
 51  2
             final Iterator<Caster> sp = ServiceRegistry
 52  2
                 .lookupProviders(Caster.class);
 53  4
             while (sp.hasNext()) {
 54  2
                 register(sp.next());
 55  
             }
 56  
         }
 57  6
     }
 58  
 
 59  
     private static <F, T> void register(final Caster<F, T> caster) {
 60  2
         final ClassPair<F, T> cp = new ClassPair<F, T>(caster.from(),
 61  2
                                                        caster.to());
 62  2
         Caster<?, ?> overwritten = casterMap.put(cp, caster);
 63  2
         if (overwritten != null) {
 64  0
             throw new OctaveCastServiceException("casterMap.containsKey(cp)");
 65  
         }
 66  2
     }
 67  
 
 68  
     /**
 69  
      * Cast and transform the object. 
 70  
      * 
 71  
      * @param <F>
 72  
      * @param <T>
 73  
      * @param toClass
 74  
      * @param from
 75  
      * @return The transformed object
 76  
      */
 77  
     public static <F extends OctaveObject, 
 78  
                    T extends OctaveObject> T cast(final Class<T> toClass, 
 79  
                                                   final F from) {
 80  594
         if (from == null) {
 81  420
             return null;
 82  
         }
 83  174
         if (toClass.isInstance(from)) {
 84  168
             return toClass.cast(from);
 85  
         }
 86  6
         final ClassPair<F, T> cp = new ClassPair<F, T>(unsafeGetClass(from),
 87  
                                                        toClass);
 88  6
         final Caster<F, T> caster = casterMapGet(cp);
 89  6
         if (caster == null) {
 90  2
             throw new OctaveClassCastException(null, from, toClass);
 91  
         }
 92  4
         return caster.cast(from);
 93  
     }
 94  
 
 95  
     @SuppressWarnings("unchecked")
 96  
     private static <F> Class<F> unsafeGetClass(final F from) {
 97  6
         return (Class<F>) from.getClass();
 98  
     }
 99  
 
 100  
     @SuppressWarnings("unchecked")
 101  
     private static <F extends OctaveObject, 
 102  
                     T extends OctaveObject> 
 103  
     Caster<F, T> casterMapGet(final ClassPair<F, T> cp) {
 104  6
         initIfNecessary();
 105  6
         if (!casterMap.containsKey(cp)) {
 106  2
             return null;
 107  
         }
 108  4
         final Caster<F, T> caster = (Caster<F, T>) casterMap.get(cp);
 109  4
         if (!caster.from().equals(cp.from)) {
 110  0
             throw new OctaveCastServiceException
 111  
                 ("!caster.from().equals(cp.from)");
 112  
         }
 113  4
         if (!caster.to().equals(cp.to)) {
 114  0
             throw new OctaveCastServiceException("!caster.to().equals(cp.to)");
 115  
         }
 116  4
         return caster;
 117  
     }
 118  
 
 119  
     /**
 120  
      * Represents a pair of classes, 
 121  
      * essentially a cast from one to the other class. 
 122  
      *
 123  
      * @param <F> from class 
 124  
      * @param <T> to   class 
 125  
      */
 126  8
     private static class ClassPair<F, T> {
 127  8
         ClassPair(final Class<F> from, final Class<T> to) {
 128  8
             this.from = from;
 129  8
             this.to = to;
 130  8
         }
 131  
 
 132  
         private final Class<F> from;
 133  
 
 134  
         private final Class<T> to;
 135  
 
 136  
         @Override
 137  
         public int hashCode() {
 138  12
             int result = 1;
 139  12
             result = PRIME * result + ((from == null) ? 0 : from.hashCode());
 140  12
             result = PRIME * result + ((to   == null) ? 0 :   to.hashCode());
 141  12
             return result;
 142  
         }
 143  
 
 144  
         @Override
 145  
         public boolean equals(final Object obj) {
 146  8
             if (this == obj) {
 147  0
                 return true;
 148  
             }
 149  8
             if (obj == null) {
 150  0
                 return false;
 151  
             }
 152  8
             if (getClass() != obj.getClass()) {
 153  0
                 return false;
 154  
             }
 155  8
             final ClassPair<?, ?> other = (ClassPair<?, ?>) obj;
 156  8
             if (from == null) {
 157  0
                 if (other.from != null) {
 158  0
                     return false;
 159  
                 }
 160  8
             } else if (!from.equals(other.from)) {
 161  0
                 return false;
 162  
             }
 163  8
             if (to == null) {
 164  0
                 if (other.to != null) {
 165  0
                     return false;
 166  
                 }
 167  8
             } else if (!to.equals(other.to)) {
 168  0
                 return false;
 169  
             }
 170  8
             return true;
 171  
         }
 172  
     }
 173  
 
 174  
 }