public synchronized ClassFieldReader getReader(
      final String className,
      final String fieldName,
      final AcceptsReadAccessor target,
      final AccessorKey.AccessorType accessorType) {
    AccessorKey key = new AccessorKey(className, fieldName, accessorType);
    FieldLookupEntry entry = (FieldLookupEntry) this.lookup.get(key);

    boolean exists = true;
    if (entry == null) {
      exists = false;
      entry = new FieldLookupEntry(new ClassFieldReader(className, fieldName));
    }

    if (this.eagerWire) {
      wire(entry.getClassFieldReader());
    }

    if (target != null) {
      target.setReadAccessor(entry.getClassFieldReader());
      entry.addAccessorTarget(target);
    }

    if (exists == false) {
      // we delay the key writing as we only want to do it if the wiring was successful
      this.lookup.put(key, entry);
    }

    return entry.getClassFieldReader();
  }
  public ClassFieldAccessor getAccessor(final String className, final String fieldName) {
    AccessorKey key = new AccessorKey(className, fieldName, AccessorKey.AccessorType.FieldAccessor);
    FieldLookupEntry entry = (FieldLookupEntry) this.lookup.get(key);
    if (entry == null) {
      entry =
          new FieldLookupEntry(
              new ClassFieldReader(className, fieldName),
              new ClassFieldWriter(className, fieldName));
      this.lookup.put(key, entry);
    }

    ClassFieldAccessor accessor =
        new ClassFieldAccessor(entry.getClassFieldReader(), entry.getClassFieldWriter());

    entry.addAccessorTarget(accessor);

    if (this.eagerWire) {
      wire(entry.getClassFieldReader());
      wire(entry.getClassFieldWriter());
    }

    return accessor;
  }
  public void merge(ClassFieldAccessorStore other) {
    for (Entry<AccessorKey, BaseLookupEntry> entry : other.lookup.entrySet()) {

      switch (entry.getValue().getAccessorType()) {
        case FieldAccessor:
          {
            FieldLookupEntry lookupEntry = (FieldLookupEntry) this.lookup.get(entry.getKey());
            if (lookupEntry == null) {
              lookupEntry = (FieldLookupEntry) entry.getValue();
              this.lookup.put(entry.getKey(), lookupEntry);
              // wire up ClassFieldReaders
              if (lookupEntry.getClassFieldReader() != null) {
                wire(lookupEntry.getClassFieldReader());
              }

              if (lookupEntry.getClassFieldWriter() != null) {
                wire(lookupEntry.getClassFieldWriter());
              }
            } else {
              // iterate through new targets adding them and wiring them up
              // to the existing ClassFieldReader, no need to wire generated accessor
              // as we know it already exists
              for (Acceptor target : entry.getValue().getAccessorTargets()) {
                if (target instanceof AcceptsReadAccessor) {
                  ((AcceptsReadAccessor) target).setReadAccessor(lookupEntry.getClassFieldReader());
                } else if (target instanceof AcceptsWriteAccessor) {
                  ((AcceptsWriteAccessor) target)
                      .setWriteAccessor(lookupEntry.getClassFieldWriter());
                }
                lookupEntry.addAccessorTarget(target);
              }
              if (lookupEntry.getClassFieldReader() != null) {
                wire(((FieldLookupEntry) entry.getValue()).getClassFieldReader());
              }
              if (lookupEntry.getClassFieldWriter() != null) {
                wire(((FieldLookupEntry) entry.getValue()).getClassFieldWriter());
              }
            }
            break;
          }

        case ClassObjectType:
          {
            ClassObjectTypeLookupEntry lookupEntry =
                (ClassObjectTypeLookupEntry) this.lookup.get(entry.getKey());
            if (lookupEntry == null) {
              // Create new entry with correct ClassObjectType and targets
              lookupEntry =
                  new ClassObjectTypeLookupEntry(
                      cache.getClassObjectType(
                          ((ClassObjectTypeLookupEntry) entry.getValue()).getClassObjectType()));

              this.lookup.put(entry.getKey(), lookupEntry);
            }

            for (Acceptor target : entry.getValue().getAccessorTargets()) {
              ((AcceptsClassObjectType) target)
                  .setClassObjectType(lookupEntry.getClassObjectType());
              lookupEntry.addAccessorTarget(target);
            }
          }

          //                case ObjectAccessor : {
          //                    ObjectExtractorLookupEntry lookupEntry = (
          // ObjectExtractorLookupEntry ) this.lookup.get( entry.getKey() );
          //                    if ( lookupEntry == null ) {
          //                        lookupEntry = ( ObjectExtractorLookupEntry )  entry.getValue();
          //                        this.lookup.put( entry.getKey(),
          //                                         lookupEntry );
          //                        wire( lookupEntry.getObjectExtractor() );
          //                    } else {
          //                        for ( Acceptor target : entry.getValue().getAccessorTargets() )
          // {
          //                            ((Declaration)target).setReadAccessor(
          // lookupEntry.getObjectExtractor() );
          //                            lookupEntry.addAccessorTarget( target );
          //                        }
          //                    }
          //                    break;
          //                }
          //                case GlobalAccessor : {
          //                    GlobalExtractorLookupEntry lookupEntry = (
          // GlobalExtractorLookupEntry ) this.lookup.get( entry.getKey() );
          //                    if ( lookupEntry == null ) {
          //                        lookupEntry = ( GlobalExtractorLookupEntry )  entry.getValue();
          //                        this.lookup.put( entry.getKey(),
          //                                         lookupEntry );
          //                        wire( lookupEntry.getGlobalExtractor() );
          //                    } else {
          //                        for ( Acceptor target : entry.getValue().getAccessorTargets() )
          // {
          //                            ((Declaration)target).setReadAccessor(
          // lookupEntry.getGlobalExtractor() );
          //                            lookupEntry.addAccessorTarget( target );
          //                        }
          //                    }
          //                    break;
          //                }
      }
    }
  }