001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 the ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.serialization.objects;
018
019import java.io.DataInputStream;
020import java.io.DataOutputStream;
021import java.io.IOException;
022import java.io.ObjectStreamConstants;
023import java.util.ArrayList;
024import java.util.List;
025
026import org.conqat.lib.commons.collections.Pair;
027import org.conqat.lib.commons.serialization.SerializedEntityParser;
028import org.conqat.lib.commons.serialization.SerializedEntityPool;
029import org.conqat.lib.commons.serialization.SerializedEntitySerializer;
030import org.conqat.lib.commons.serialization.classes.SerializedClass;
031import org.conqat.lib.commons.serialization.classes.SerializedClassBase;
032import org.conqat.lib.commons.serialization.classes.SerializedFieldBase;
033import org.conqat.lib.commons.string.StringUtils;
034
035/**
036 * A serialized object.
037 */
038public class SerializedObject extends SerializedObjectBase {
039
040        /**
041         * The field values for this class organized as field sets. Each field set
042         * corresponds to one class in the class hierarchy (from sub class to super
043         * class).
044         */
045        private final List<SerializedClassValues> fieldSets = new ArrayList<>();
046
047        /** Constructor. */
048        public SerializedObject(DataInputStream din, SerializedEntityPool pool, SerializedEntityParser parser,
049                        int classHandle) throws IOException {
050                super(pool, classHandle);
051
052                for (SerializedClass serializedClass : getPlainClassHierarchy()) {
053                        fieldSets.add(new SerializedClassValues(serializedClass, din, parser));
054                }
055        }
056
057        /** Constructor. */
058        public SerializedObject(int classHandle, List<SerializedClassValues> fieldSets, SerializedEntityPool pool) {
059                super(pool, classHandle);
060                this.fieldSets.addAll(fieldSets);
061        }
062
063        /** {@inheritDoc} */
064        @Override
065        protected byte getObjectTagConstant() {
066                return ObjectStreamConstants.TC_OBJECT;
067        }
068
069        /** {@inheritDoc} */
070        @Override
071        protected void serializeObjectContent(DataOutputStream dos, SerializedEntitySerializer serializer)
072                        throws IOException {
073                int index = 0;
074                for (SerializedClass serializedClass : getPlainClassHierarchy()) {
075                        SerializedClassValues fieldSet = fieldSets.get(index++);
076                        fieldSet.serialize(serializedClass, pool, dos, serializer);
077                }
078        }
079
080        /** {@inheritDoc} */
081        @Override
082        public String toString() {
083                StringBuilder builder = new StringBuilder();
084                String className = "<unknown>";
085                try {
086                        className = pool.getEntity(classHandle, SerializedClassBase.class).toString();
087                        builder.append("Object " + getHandle() + ": " + className + StringUtils.LINE_SEPARATOR);
088
089                        int index = 0;
090                        for (SerializedClass serializedClass : getPlainClassHierarchy()) {
091                                appendFieldSet(fieldSets.get(index++), serializedClass, builder);
092                        }
093                } catch (IOException e) {
094                        builder.append("Could not resolve hierarchy for class " + className + ": " + e.getMessage());
095                }
096                return builder.toString();
097        }
098
099        /** Appends the information on a field set to the given builder. */
100        private static void appendFieldSet(SerializedClassValues fieldSet, SerializedClass serializedClass,
101                        StringBuilder builder) {
102                if (fieldSet.hasFieldValues()) {
103                        int fieldIndex = 0;
104                        for (SerializedFieldBase field : serializedClass.getFields()) {
105                                builder.append("  " + serializedClass.getName() + "#" + field.getName() + ": "
106                                                + fieldSet.getValue(fieldIndex++) + StringUtils.LINE_SEPARATOR);
107                        }
108                } else {
109                        builder.append("  " + serializedClass.getName() + ": <no data>");
110                }
111        }
112
113        /**
114         * Return the value object for the field with the given name.
115         * 
116         * @return The fields value or null if no field with the name was found
117         */
118        public Object getFieldValue(String name) throws IOException {
119                Pair<SerializedClassValues, Integer> fieldSetAndValue = getFieldSetAndIndex(name);
120                if (fieldSetAndValue == null) {
121                        return null;
122                }
123                return fieldSetAndValue.getFirst().getValue(fieldSetAndValue.getSecond());
124        }
125
126        /** Sets the value object for the field with the given name. */
127        public void setFieldValue(String name, Object value) throws IOException {
128                Pair<SerializedClassValues, Integer> fieldSetAndValue = getFieldSetAndIndex(name);
129                if (fieldSetAndValue != null) {
130                        fieldSetAndValue.getFirst().setValue(fieldSetAndValue.getSecond(), value);
131                }
132        }
133
134        /**
135         * Removes the value object for the field with the given name. This does not
136         * only set the vlaue to 0 or null, but corresponds to a deletion of the field.
137         */
138        public void removeFieldValue(String name) throws IOException {
139                Pair<SerializedClassValues, Integer> fieldSetAndValue = getFieldSetAndIndex(name);
140                if (fieldSetAndValue != null) {
141                        fieldSetAndValue.getFirst().removeValue(fieldSetAndValue.getSecond());
142                }
143        }
144
145        /**
146         * Returns the field set and index within this fieldset for a field with given
147         * name. Returns null if none is found.
148         */
149        private Pair<SerializedClassValues, Integer> getFieldSetAndIndex(String fieldName) throws IOException {
150                int classIndex = 0;
151                for (SerializedClass serializedClass : getPlainClassHierarchy()) {
152                        SerializedFieldBase field = serializedClass.getField(fieldName);
153                        if (field != null) {
154                                int index = serializedClass.getFields().indexOf(field);
155                                SerializedClassValues fieldSet = fieldSets.get(classIndex);
156                                return new Pair<>(fieldSet, index);
157                        }
158                        classIndex++;
159                }
160                return null;
161        }
162
163        /** Returns number of field sets. */
164        public int getFieldSetCount() {
165                return fieldSets.size();
166        }
167
168        /** Returns field sets of given index. */
169        public SerializedClassValues getFieldSet(int index) {
170                return fieldSets.get(index);
171        }
172}