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 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      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 = ServiceRegistry
52  		.lookupProviders(Caster.class);
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       * @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          if (from == null) {
81              return null;
82          }
83          if (toClass.isInstance(from)) {
84              return toClass.cast(from);
85          }
86          final ClassPair<F, T> cp = new ClassPair<F, T>(unsafeGetClass(from),
87  						       toClass);
88          final Caster<F, T> caster = casterMapGet(cp);
89          if (caster == null) {
90              throw new OctaveClassCastException(null, from, toClass);
91          }
92          return caster.cast(from);
93      }
94  
95      @SuppressWarnings("unchecked")
96      private static <F> Class<F> unsafeGetClass(final F from) {
97          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         initIfNecessary();
105         if (!casterMap.containsKey(cp)) {
106             return null;
107         }
108         final Caster<F, T> caster = (Caster<F, T>) casterMap.get(cp);
109         if (!caster.from().equals(cp.from)) {
110             throw new OctaveCastServiceException
111 		("!caster.from().equals(cp.from)");
112         }
113         if (!caster.to().equals(cp.to)) {
114             throw new OctaveCastServiceException("!caster.to().equals(cp.to)");
115         }
116         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     private static class ClassPair<F, T> {
127         ClassPair(final Class<F> from, final Class<T> to) {
128             this.from = from;
129             this.to = to;
130         }
131 
132         private final Class<F> from;
133 
134         private final Class<T> to;
135 
136         @Override
137         public int hashCode() {
138             int result = 1;
139             result = PRIME * result + ((from == null) ? 0 : from.hashCode());
140             result = PRIME * result + ((to   == null) ? 0 :   to.hashCode());
141             return result;
142         }
143 
144 	@Override
145 	public boolean equals(final Object obj) {
146             if (this == obj) {
147                 return true;
148             }
149             if (obj == null) {
150                 return false;
151             }
152             if (getClass() != obj.getClass()) {
153                 return false;
154             }
155             final ClassPair<?, ?> other = (ClassPair<?, ?>) obj;
156             if (from == null) {
157                 if (other.from != null) {
158                     return false;
159                 }
160             } else if (!from.equals(other.from)) {
161                 return false;
162             }
163             if (to == null) {
164                 if (other.to != null) {
165                     return false;
166                 }
167             } else if (!to.equals(other.to)) {
168                 return false;
169             }
170             return true;
171         }
172     }
173 
174 }