001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2013 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.messaging.jmf;
022
023import java.io.IOException;
024import java.io.OutputStream;
025import java.lang.reflect.Field;
026import java.util.HashMap;
027import java.util.IdentityHashMap;
028import java.util.Map;
029
030import org.granite.messaging.jmf.codec.StandardCodec;
031import org.granite.messaging.jmf.reflect.Reflection;
032
033/**
034 * @author Franck WOLFF
035 */
036public class JMFSerializer implements OutputContext {
037        
038        ///////////////////////////////////////////////////////////////////////////
039        // Fields
040
041        protected final Map<String, Integer> storedStrings = new HashMap<String, Integer>(256);
042        protected final Map<Object, Integer> storedObjects = new IdentityHashMap<Object, Integer>(256);
043        
044    protected final OutputStream outputStream;
045    protected final SharedContext context;
046    
047    protected final CodecRegistry codecRegistry;
048        
049        ///////////////////////////////////////////////////////////////////////////
050        // Initialization
051
052        public JMFSerializer(OutputStream outputStream, SharedContext context) {
053                this.outputStream = outputStream;
054                this.codecRegistry = context.getCodecRegistry();
055                this.context = context;
056                
057                for (String s : context.getDefaultStoredStrings())
058                        addToStoredStrings(s);
059        }
060        
061        ///////////////////////////////////////////////////////////////////////////
062        // ObjectOutput implementation
063
064        public void writeBoolean(boolean v) throws IOException {
065                codecRegistry.getBooleanCodec().encodePrimitive(this, v);
066        }
067
068        public void writeByte(int v) throws IOException {
069                codecRegistry.getByteCodec().encodePrimitive(this, v);
070        }
071
072        public void writeShort(int v) throws IOException {
073                codecRegistry.getShortCodec().encodePrimitive(this, v);
074        }
075
076        public void writeChar(int v) throws IOException {
077                codecRegistry.getCharacterCodec().encodePrimitive(this, v);
078        }
079        
080        public void writeInt(int v) throws IOException {
081                codecRegistry.getIntegerCodec().encodePrimitive(this, v);
082        }
083
084        public void writeLong(long v) throws IOException {
085                codecRegistry.getLongCodec().encodePrimitive(this, v);
086        }
087
088        public void writeFloat(float v) throws IOException {
089                codecRegistry.getFloatCodec().encodePrimitive(this, v);
090        }
091
092        public void writeDouble(double v) throws IOException {
093                codecRegistry.getDoubleCodec().encodePrimitive(this, v);
094        }
095
096        public void writeUTF(String s) throws IOException {
097                if (s == null)
098                        codecRegistry.getNullCodec().encode(this, s);
099                else
100                        codecRegistry.getStringCodec().encode(this, s);
101        }
102
103        public void writeObject(Object obj) throws IOException {
104                StandardCodec<Object> codec = codecRegistry.getCodec(obj);
105                if (codec == null)
106                        throw new JMFEncodingException("Unsupported Java class: " + obj);
107                
108                try {
109                        codec.encode(this, obj);
110                }
111                catch (IllegalAccessException e) {
112                        throw new IOException(e);
113                }
114        }
115
116        public void flush() throws IOException {
117                outputStream.flush();
118        }
119
120        public void close() throws IOException {
121                outputStream.close();
122        }
123        
124        ///////////////////////////////////////////////////////////////////////////
125        // ObjectOutput implementation (unsupported, marked at deprecated)
126
127        @Deprecated
128        public void write(int b) throws IOException {
129                throw new UnsupportedOperationException("Use writeByte(b)");
130        }
131
132        @Deprecated
133        public void write(byte[] b) throws IOException {
134                throw new UnsupportedOperationException("Use writeObject(b)");
135        }
136
137        @Deprecated
138        public void write(byte[] b, int off, int len) throws IOException {
139                throw new UnsupportedOperationException("Use writeObject(Arrays.copyOfRange(b, off, off+len))");
140        }
141
142        @Deprecated
143        public void writeBytes(String s) throws IOException {
144                throw new UnsupportedOperationException("Use writeUTF(s)");
145        }
146
147        @Deprecated
148        public void writeChars(String s) throws IOException {
149                throw new UnsupportedOperationException("Use writeUTF(s)");
150        }
151        
152        ///////////////////////////////////////////////////////////////////////////
153        // OutputContext implementation
154
155        public SharedContext getSharedContext() {
156                return context;
157        }
158
159        public OutputStream getOutputStream() {
160                return outputStream;
161        }
162
163        public void addToStoredStrings(String s) {
164        if (s != null && !storedStrings.containsKey(s)) {
165            Integer index = Integer.valueOf(storedStrings.size());
166            storedStrings.put(s, index);
167        }
168    }
169
170        public int indexOfStoredStrings(String s) {
171        if (s != null) {
172                Integer index = storedStrings.get(s);
173                if (index != null)
174                        return index.intValue();
175        }
176        return -1;
177    }
178
179        public void addToStoredObjects(Object o) {
180        if (o != null && !storedObjects.containsKey(o)) {
181            Integer index = Integer.valueOf(storedObjects.size());
182            storedObjects.put(o, index);
183        }
184    }
185
186        public int indexOfStoredObjects(Object o) {
187        if (o != null) {
188                Integer index = storedObjects.get(o);
189                if (index != null)
190                        return index.intValue();
191        }
192                return -1;
193    }
194        
195        ///////////////////////////////////////////////////////////////////////////
196        // ExtendedObjectOutput implementation
197
198        public Reflection getReflection() {
199                return context.getReflection();
200        }
201
202        public String getAlias(String className) {
203                return context.getAlias(className);
204        }
205
206        public void getAndWriteField(Object obj, Field field) throws IOException, IllegalAccessException {
207                if (field.getType().isPrimitive())
208                        codecRegistry.getPrimitiveFieldCodec(field.getType()).encodePrimitive(this, obj, field);
209                else
210                        writeObject(field.get(obj));
211        }
212}