com.gemstone.gemfire.pdx.internal.PdxType Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gemfire-core Show documentation
Show all versions of gemfire-core Show documentation
SnappyData store based off Pivotal GemFireXD
The newest version!
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.pdx.internal;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicReference;
import com.gemstone.gemfire.DataSerializable;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.internal.ClassPathLoader;
import com.gemstone.gemfire.internal.InternalDataSerializer;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.pdx.PdxFieldAlreadyExistsException;
import com.gemstone.gemfire.pdx.PdxSerializationException;
import com.gemstone.gemfire.pdx.internal.AutoSerializableManager.AutoClassInfo;
public class PdxType implements DataSerializable {
private static final long serialVersionUID = -1950047949756115279L;
private int cachedHash = 0;
private int typeId;
private String className;
private boolean noDomainClass;
/**
* A count of the total number of variable length field offsets.
*/
private int vlfCount;
private final ArrayList fields = new ArrayList();
private final transient Map fieldsMap = new HashMap();
private transient volatile SortedSet sortedIdentityFields;
public PdxType() {
// for deserialization
}
public PdxType(String name, boolean expectDomainClass) {
this.className = name;
this.noDomainClass = !expectDomainClass;
}
public PdxType(PdxType copy) {
this.typeId = copy.typeId;
this.className = copy.className;
this.noDomainClass = copy.noDomainClass;
this.vlfCount = copy.vlfCount;
for (PdxField ft: copy.fields) {
addField(ft);
}
}
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
this.className = DataSerializer.readString(in);
this.noDomainClass = DataSerializer.readPrimitiveBoolean(in);
this.typeId = in.readInt();
this.vlfCount = in.readInt();
int arrayLen = InternalDataSerializer.readArrayLength(in);
for (int i = 0; i < arrayLen; i++) {
PdxField vft = new PdxField();
vft.fromData(in);
addField(vft);
}
}
public void toData(DataOutput out) throws IOException {
DataSerializer.writeString(this.className, out);
DataSerializer.writePrimitiveBoolean(this.noDomainClass, out);
out.writeInt(this.typeId);
out.writeInt(this.vlfCount);
InternalDataSerializer.writeArrayLength(this.fields.size(), out);
for (int i = 0; i < this.fields.size(); i++) {
PdxField vft = this.fields.get(i);
vft.toData(out);
}
}
@Override
public int hashCode() {
int hash = cachedHash;
if(hash == 0) {
hash = 1;
hash = hash * 31 + this.className.hashCode();
for(PdxField field : this.fields) {
hash = hash * 31 + field.hashCode();
}
if(hash == 0) {
hash = 1;
}
cachedHash = hash;
}
return cachedHash;
}
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other == null || !(other instanceof PdxType)) {
return false;
}
// Note: do not compare type id in equals
PdxType otherVT = (PdxType)other;
if (!(this.className.equals(otherVT.className))) {
return false;
}
if (this.noDomainClass != otherVT.noDomainClass) {
return false;
}
if (otherVT.fields.size() != this.fields.size()
|| otherVT.vlfCount != this.vlfCount) {
return false;
}
for (int i = 0; i < this.fields.size(); i++) {
if (!this.fields.get(i).equals(otherVT.fields.get(i))) {
return false;
}
}
return true;
}
/**
* Return true if two pdx types have same class name and the same fields
* but, unlike equals, field order does not matter.
* Note a type that expects a domain class can be compatible with
* one that does not expect a domain class.
* @param other the other pdx type
* @return true if two pdx types are compatible.
*/
public boolean compatible(PdxType other) {
if (other == null) return false;
if(!getClassName().equals(other.getClassName())) {
return false;
}
Collection myFields = getSortedFields();
Collection otherFields = other.getSortedFields();
return myFields.equals(otherFields);
}
public int getVariableLengthFieldCount() {
return this.vlfCount;
}
public String getClassName() {
return this.className;
}
public Class getPdxClass() {
try {
return InternalDataSerializer.getCachedClass(getClassName());
} catch (Exception e) {
PdxSerializationException ex = new PdxSerializationException(
LocalizedStrings.DataSerializer_COULD_NOT_CREATE_AN_INSTANCE_OF_A_CLASS_0
.toLocalizedString(getClassName()), e);
throw ex;
}
}
public boolean getNoDomainClass() {
return this.noDomainClass;
}
public int getTypeId() {
return this.typeId;
}
public int getDSId() {
return this.typeId >> 24;
}
public int getTypeNum() {
return this.typeId & 0x00FFFFFF;
}
public void setTypeId(int tId) {
this.typeId = tId;
}
/*
* This method is use to create Pdxtype for which classname is not available; while creating PdxInstance
*/
public void setClassName(String className) {
this.className = className;
}
public void addField(PdxField ft) {
if (this.fieldsMap.put(ft.getFieldName(), ft) != null) {
throw new PdxFieldAlreadyExistsException("The field \"" + ft.getFieldName() + "\" already exists.");
}
this.fields.add(ft);
}
public void initialize(PdxWriterImpl writer) {
this.vlfCount = writer.getVlfCount();
int size = this.fields.size();
int fixedLenFieldOffset = 0;
boolean seenVariableLenType = false;
for (int i = 0; i < size; i++) {
PdxField vft = this.fields.get(i);
// System.out.println(i + ": " + vft);
if (vft.isVariableLengthType()) {
if (seenVariableLenType) {
vft.setVlfOffsetIndex(vft.getVarLenFieldSeqId());
} else {
vft.setRelativeOffset(fixedLenFieldOffset);
vft.setVlfOffsetIndex(-1);
}
seenVariableLenType = true;
} else if (seenVariableLenType) {
PdxField tmp = null;
int minusOffset = vft.getFieldType().getWidth();
for (int j = (i+1); j < size; j++) {
tmp = this.fields.get(j);
if (tmp.isVariableLengthType()) {
break;
} else {
minusOffset += tmp.getFieldType().getWidth();
}
}
if (tmp != null && tmp.isVariableLengthType()) {
vft.setRelativeOffset(-minusOffset);
vft.setVlfOffsetIndex(tmp.getVarLenFieldSeqId());
} else {
vft.setRelativeOffset(-minusOffset);
vft.setVlfOffsetIndex(-1); // From the byte after the last field data byte
}
} else {
vft.setRelativeOffset(fixedLenFieldOffset);
fixedLenFieldOffset += vft.getFieldType().getWidth();
}
}
// no longer mark identity fields implicitly. Fixes bug 42976.
// System.out.println("Printing the position array:");
// for (int i = 0; i < this.positionArray.length; i++) {
// System.out.println("[" + i + "][0]=" + this.positionArray[i][0] + ", ["
// + i + "][1]=" + this.positionArray[i][1]);
// }
}
public PdxField getPdxField(String fieldName) {
return this.fieldsMap.get(fieldName);
}
public List getFields() {
return Collections.unmodifiableList(this.fields);
}
public PdxField getPdxFieldByIndex(int index) {
return this.fields.get(index);
}
public int getFieldCount() {
return this.fields.size();
}
public String toFormattedString() {
StringBuffer sb = new StringBuffer("PdxType[\n ");
sb.append("dsid=").append(getDSId());
sb.append("typenum=").append(getTypeNum());
sb.append(", name=").append(this.className);
sb.append(", fields=[");
for (PdxField vft : fields) {
sb.append("\n ");
sb.append(/*vft.getFieldName() + ":" + vft.getTypeId()*/ vft.toString());
}
sb.append("]]");
return sb.toString();
}
public String toString() {
StringBuffer sb = new StringBuffer("PdxType[");
sb.append("dsid=").append(getDSId());
sb.append(",typenum=").append(getTypeNum());
sb.append(",name=").append(this.className);
sb.append(",fields=[");
for (PdxField vft : fields) {
sb.append(/*vft.getFieldName() + ":" + vft.getTypeId()*/ vft.toString()).append(", ");
}
sb.append("]]");
return sb.toString();
}
/**
*
* @param readFields the fields that have been read
* @return a List of fields that have not been read (may be empty).
*/
public List getUnreadFieldIndexes(List readFields) {
ArrayList result = new ArrayList();
for (PdxField ft: this.fields) {
if (!readFields.contains(ft.getFieldName())) {
result.add(ft.getFieldIndex());
}
}
return result;
}
/**
* Return true if the this type has a field that the other type does not have.
* @param other the type we are comparing to
* @return true if the this type has a field that the other type does not have.
*/
public boolean hasExtraFields(PdxType other) {
for (PdxField ft: this.fields) {
if (other.getPdxField(ft.getFieldName()) == null) {
return true;
}
}
return false;
}
public SortedSet getSortedIdentityFields() {
if(this.sortedIdentityFields == null) {
TreeSet sortedSet = new TreeSet();
for(PdxField field: fields) {
if(field.isIdentityField()) {
sortedSet.add(field);
}
}
//If we don't find any marked identity fields, use all of the fields.
if(sortedSet.isEmpty()) {
sortedSet.addAll(this.fields);
}
this.sortedIdentityFields = sortedSet;
}
return this.sortedIdentityFields;
}
public Collection getSortedFields() {
TreeSet sortedSet = new TreeSet(this.fields);
return new ArrayList(sortedSet);
}
public List getFieldNames() {
ArrayList result = new ArrayList(this.fields.size());
for (PdxField f: this.fields) {
result.add(f.getFieldName());
}
return Collections.unmodifiableList(result);
}
/**
* Used to optimize auto deserialization
*/
private transient final AtomicReference autoClassInfo = new AtomicReference();
public void setAutoInfo(AutoClassInfo autoClassInfo) {
this.autoClassInfo.set(autoClassInfo);
}
public AutoClassInfo getAutoInfo(Class c) {
AutoClassInfo ci = this.autoClassInfo.get();
if (ci != null) {
Class lastClassAutoSerialized = ci.getInfoClass();
if (c.equals(lastClassAutoSerialized)) {
return ci;
} else {
if (lastClassAutoSerialized == null) {
this.autoClassInfo.compareAndSet(ci, null);
}
}
}
return null;
}
}