View Javadoc
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 java.util.ServiceLoader;
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      private Cast() {
43          throw new UnsupportedOperationException("Do not instantiate");
44      }
45  
46      @SuppressWarnings("unchecked")
47      private static synchronized void initIfNecessary() {
48          if (casterMap == null) {
49              casterMap = new HashMap<ClassPair<?, ?>, Caster<?, ?>>();
50              @SuppressWarnings("rawtypes")
51              final Iterator<Caster> sp = 
52                 ServiceLoader.load(Caster.class).iterator();
53  	    while (sp.hasNext()) {
54                  register(sp.next());
55              }
56          }
57      }
58  
59      private static <F, T> void register(final Caster<F, T> caster) {
60          final ClassPair<F, T> cp = new ClassPair<F, T>(caster.from(),
61  						       caster.to());
62  	Caster<?, ?> overwritten = casterMap.put(cp, caster);
63          if (overwritten != null) {
64              throw new OctaveCastServiceException("casterMap.containsKey(cp)");
65          }
66      }
67  
68      /**
69       * Cast and transform the object. 
70       * 
71       * @param <F>
72       *    the class type of the object <code>from</code> to be cast.
73       * @param <T>
74       *    the target class <code>toClass</code> as a type variable.
75       * @param toClass
76       *    the class <code>from</code>
77       * @param from
78       *    the object to be cast or <code>null</code>.
79       * @return
80       *    The object of type <code>toClass</code> which is by casting <code>from</code>
81       *    or <code>null</code> for <code>from == null</code>.
82       */
83      public static <F extends OctaveObject, 
84  		   T extends OctaveObject> T cast(final Class<T> toClass, 
85  						  final F from) {
86          if (from == null) {
87              return null;
88          }
89          if (toClass.isInstance(from)) {
90              return toClass.cast(from);
91          }
92          final ClassPair<F, T> cp = new ClassPair<F, T>(unsafeGetClass(from),
93  						       toClass);
94          final Caster<F, T> caster = casterMapGet(cp);
95          if (caster == null) {
96              throw new OctaveClassCastException(null, from, toClass);
97          }
98          return caster.cast(from);
99      }
100 
101     @SuppressWarnings("unchecked")
102     private static <F> Class<F> unsafeGetClass(final F from) {
103         return (Class<F>) from.getClass();
104     }
105 
106     @SuppressWarnings("unchecked")
107     private static <F extends OctaveObject, 
108 		    T extends OctaveObject> 
109     Caster<F, T> casterMapGet(final ClassPair<F, T> cp) {
110         initIfNecessary();
111         if (!casterMap.containsKey(cp)) {
112             return null;
113         }
114         final Caster<F, T> caster = (Caster<F, T>) casterMap.get(cp);
115         if (!caster.from().equals(cp.from)) {
116             throw new OctaveCastServiceException
117 		("!caster.from().equals(cp.from)");
118         }
119         if (!caster.to().equals(cp.to)) {
120             throw new OctaveCastServiceException("!caster.to().equals(cp.to)");
121         }
122         return caster;
123     }
124 
125     /**
126      * Represents a pair of classes, 
127      * essentially a cast from one to the other class. 
128      *
129      * @param <F> from class 
130      * @param <T> to   class 
131      */
132     private static class ClassPair<F, T> {
133         ClassPair(final Class<F> from, final Class<T> to) {
134             this.from = from;
135             this.to = to;
136         }
137 
138         private final Class<F> from;
139 
140         private final Class<T> to;
141 
142         @Override
143         public int hashCode() {
144             int result = 1;
145             result = PRIME * result + ((from == null) ? 0 : from.hashCode());
146             result = PRIME * result + ((to   == null) ? 0 :   to.hashCode());
147             return result;
148         }
149 
150 	@Override
151 	public boolean equals(final Object obj) {
152             if (this == obj) {
153                 return true;
154             }
155             if (obj == null) {
156                 return false;
157             }
158             if (getClass() != obj.getClass()) {
159                 return false;
160             }
161             final ClassPair<?, ?> other = (ClassPair<?, ?>) obj;
162             if (from == null) {
163                 if (other.from != null) {
164                     return false;
165                 }
166             } else if (!from.equals(other.from)) {
167                 return false;
168             }
169             if (to == null) {
170                 if (other.to != null) {
171                     return false;
172                 }
173             } else if (!to.equals(other.to)) {
174                 return false;
175             }
176             return true;
177         }
178     }
179 
180 }