All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.pdx.internal.PdxType Maven / Gradle / Ivy

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;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy