Index: src/java/org/apache/lucene/index/FieldsReader.java
===================================================================
--- src/java/org/apache/lucene/index/FieldsReader.java	(revision 917019)
+++ src/java/org/apache/lucene/index/FieldsReader.java	Sat Mar 13 17:51:16 CET 2010
@@ -316,7 +316,7 @@
       Field.Index index = Field.Index.toIndex(fi.isIndexed, tokenize);
       Field.TermVector termVector = Field.TermVector.toTermVector(fi.storeTermVector, fi.storeOffsetWithTermVector, fi.storePositionWithTermVector);
 
-      AbstractField f;
+      Field f;
       if (compressed) {
         int toRead = fieldsStream.readVInt();
         long pointer = fieldsStream.getFilePointer();
@@ -361,7 +361,7 @@
       Field.Index index = Field.Index.toIndex(fi.isIndexed, tokenize);
       Field.TermVector termVector = Field.TermVector.toTermVector(fi.storeTermVector, fi.storeOffsetWithTermVector, fi.storePositionWithTermVector);
 
-      AbstractField f;
+      Field f;
       if (compressed) {
         int toRead = fieldsStream.readVInt();
 
@@ -408,7 +408,7 @@
    * A Lazy implementation of Fieldable that differs loading of fields until asked for, instead of when the Document is
    * loaded.
    */
-  private class LazyField extends AbstractField implements Fieldable {
+  private class LazyField extends Field implements Fieldable {
     private int toRead;
     private long pointer;
     /** @deprecated Only kept for backward-compatbility with <3.0 indexes. Will be removed in 4.0. */
@@ -416,7 +416,7 @@
     private boolean isCompressed;
 
     public LazyField(String name, Field.Store store, int toRead, long pointer, boolean isBinary, boolean isCompressed) {
-      super(name, store, Field.Index.NO, Field.TermVector.NO);
+      super(name, null, store, Field.Index.NO, Field.TermVector.NO);
       this.toRead = toRead;
       this.pointer = pointer;
       this.isBinary = isBinary;
@@ -427,7 +427,7 @@
     }
 
     public LazyField(String name, Field.Store store, Field.Index index, Field.TermVector termVector, int toRead, long pointer, boolean isBinary, boolean isCompressed) {
-      super(name, store, index, termVector);
+      super(name, null, store, index, termVector);
       this.toRead = toRead;
       this.pointer = pointer;
       this.isBinary = isBinary;
Index: src/java/org/apache/lucene/document/Field.java
===================================================================
--- src/java/org/apache/lucene/document/Field.java	(revision 893177)
+++ src/java/org/apache/lucene/document/Field.java	Sat Mar 13 17:51:16 CET 2010
@@ -32,7 +32,7 @@
   index, so that they may be returned with hits on the document.
   */
 
-public final class Field extends AbstractField implements Fieldable, Serializable {
+public class Field implements Fieldable, Serializable {
   
   /** Specifies whether and how a field should be stored. */
   public static enum Store {
@@ -260,8 +260,28 @@
     public abstract boolean withPositions();
     public abstract boolean withOffsets();
   }
-  
+
+  protected String name;
+  protected boolean storeTermVector = false;
+  protected boolean storeOffsetWithTermVector = false;
+  protected boolean storePositionWithTermVector = false;
+  protected boolean omitNorms = false;
+  protected boolean isStored = false;
+  protected boolean isIndexed = true;
+  protected boolean isTokenized = true;
+  protected boolean isBinary = false;
+  protected boolean lazy = false;
+  protected boolean omitTermFreqAndPositions = false;
+  protected float boost = 1.0f;
+  // the data object for all different kind of field values
+  protected Object fieldsData = null;
+  // pre-analyzed tokenStream for indexed fields
+  protected TokenStream tokenStream;
+  // length/offset for all primitive types
+  protected int binaryLength;
+  protected int binaryOffset;
   
+  
   /** The value of the field as a String, or null.  If null, the Reader value or
    * binary value is used.  Exactly one of stringValue(),
    * readerValue(), and getBinaryValue() must be set. */
@@ -392,9 +412,7 @@
   public Field(String name, boolean internName, String value, Store store, Index index, TermVector termVector) {
     if (name == null)
       throw new NullPointerException("name cannot be null");
-    if (value == null)
-      throw new NullPointerException("value cannot be null");
-    if (name.length() == 0 && value.length() == 0)
+    if (name.length() == 0 && (value == null || value.length() == 0))
       throw new IllegalArgumentException("name and value cannot both be empty");
     if (index == Index.NO && store == Store.NO)
       throw new IllegalArgumentException("it doesn't make sense to have a field that "
@@ -594,4 +612,274 @@
     
     setStoreTermVector(TermVector.NO);
   }
+
+  // ================================================= Getters / Setters =============================================
+
+  protected void setStoreTermVector(Field.TermVector termVector) {
+    this.storeTermVector = termVector.isStored();
+    this.storePositionWithTermVector = termVector.withPositions();
+    this.storeOffsetWithTermVector = termVector.withOffsets();
-}
+  }
+
+  /**
+   * Sets the boost factor hits on this field.  This value will be
+   * multiplied into the score of all hits on this this field of this
+   * document.
+   *
+   * <p>The boost is multiplied by {@link org.apache.lucene.document.Document#getBoost()} of the document
+   * containing this field.  If a document has multiple fields with the same
+   * name, all such values are multiplied together.  This product is then
+   * used to compute the norm factor for the field.  By
+   * default, in the {@link
+   * org.apache.lucene.search.Similarity#computeNorm(String,
+   * org.apache.lucene.index.FieldInvertState)} method, the boost value is multipled
+   * by the {@link
+   * org.apache.lucene.search.Similarity#lengthNorm(String,
+   * int)} and then
+   * rounded by {@link org.apache.lucene.search.Similarity#encodeNormValue(float)} before it is stored in the
+   * index.  One should attempt to ensure that this product does not overflow
+   * the range of that encoding.
+   *
+   * @see org.apache.lucene.document.Document#setBoost(float)
+   * @see org.apache.lucene.search.Similarity#computeNorm(String, org.apache.lucene.index.FieldInvertState)
+   * @see org.apache.lucene.search.Similarity#encodeNormValue(float)
+   */
+  public void setBoost(float boost) {
+    this.boost = boost;
+  }
+
+  /**
+   * Returns the boost factor for hits for this field.
+   *
+   * <p>The default value is 1.0.
+   *
+   * <p>Note: this value is not stored directly with the document in the index.
+   * Documents returned from {@link org.apache.lucene.index.IndexReader#document(int)} and
+   * {@link org.apache.lucene.search.Searcher#doc(int)} may thus not have the same value present as when
+   * this field was indexed.
+   *
+   * @see #setBoost(float)
+   */
+  public float getBoost() {
+    return boost;
+  }
+
+  /**
+   * Returns the name of the field as an interned string.
+   * For example "date", "title", "body", ...
+   */
+  public String name() {
+    return name;
+  }
+
+  /**
+   * True iff the value of the field is to be stored in the index for return
+   * with search hits.  It is an error for this to be true if a field is
+   * Reader-valued.
+   */
+  public boolean isStored() {
+    return isStored;
+  }
+
+  /**
+   * True iff the value of the field is to be indexed, so that it may be
+   * searched on.
+   */
+  public boolean isIndexed() {
+    return isIndexed;
+  }
+
+  /**
+   * True iff the value of the field should be tokenized as text prior to
+   * indexing.  Un-tokenized fields are indexed as a single word and may not be
+   * Reader-valued.
+   */
+  public boolean isTokenized() {
+    return isTokenized;
+  }
+
+  /**
+   * True iff the term or terms used to index this field are stored as a term
+   *  vector, available from {@link org.apache.lucene.index.IndexReader#getTermFreqVector(int,String)}.
+   *  These methods do not provide access to the original content of the field,
+   *  only to terms used to index it. If the original content must be
+   *  preserved, use the <code>stored</code> attribute instead.
+   *
+   * @see org.apache.lucene.index.IndexReader#getTermFreqVector(int, String)
+   */
+  public boolean isTermVectorStored() {
+    return storeTermVector;
+  }
+
+  /**
+   * True iff terms are stored as term vector together with their offsets
+   * (start and end position in source text).
+   */
+  public boolean isStoreOffsetWithTermVector() {
+    return storeOffsetWithTermVector;
+  }
+
+  /**
+   * True iff terms are stored as term vector together with their token positions.
+   */
+  public boolean isStorePositionWithTermVector() {
+    return storePositionWithTermVector;
+  }
+
+  /**
+   * True iff the value of the filed is stored as binary
+   */
+  public boolean isBinary() {
+    return isBinary;
+  }
+
+  /**
+   * True if norms are omitted for this indexed field
+   */
+  public boolean getOmitNorms() {
+    return omitNorms;
+  }
+
+  /**
+   * Expert:
+   *
+   * If set, omit normalization factors associated with this indexed field.
+   * This effectively disables indexing boosts and length normalization for this field.
+   */
+  public void setOmitNorms(boolean omitNorms) {
+    this.omitNorms = omitNorms;
+  }
+
+  public boolean isLazy() {
+    return lazy;
+  }
+
+  /**
+   * Returns offset into byte[] segment that is used as value, if Field is not binary
+   * returned value is undefined
+   * 
+   * @return index of the first character in byte[] segment that represents this Field value
+   */
+  public int getBinaryOffset() {
+    return binaryOffset;
+  }
+
+  /**
+   * Returns length of byte[] segment that is used as value, if Field is not binary
+   * returned value is undefined
+   * 
+   * @return length of byte[] segment that represents this Field value
+   */
+  public int getBinaryLength() {
+    if (isBinary) {
+      return binaryLength;
+    }
+    if (fieldsData instanceof byte[]) {
+      return ((byte[]) fieldsData).length;
+    }
+    return 0;
+  }
+
+  /**
+   * Return the raw byte[] for the binary field.  Note that
+   * you must also call {@link #getBinaryLength} and {@link
+   * #getBinaryOffset} to know which range of bytes in this
+   * returned array belong to the field.
+   * 
+   * @return reference to the Field value as byte[].
+   */
+  public byte[] getBinaryValue() {
+    return getBinaryValue(null);
+  }
+
+  public byte[] getBinaryValue(byte[] result) {
+    return isBinary || fieldsData instanceof byte[] ? (byte[]) fieldsData : null;
+  }
+
+  /**
+   * @see #setOmitTermFreqAndPositions
+   */
+  public boolean getOmitTermFreqAndPositions() {
+    return omitTermFreqAndPositions;
+  }
+
+  /**
+   * Expert:
+   *
+   * If set, omit term freq, positions and payloads from
+   * postings for this field.
+   *
+   * <p><b>NOTE</b>: While this option reduces storage space
+   * required in the index, it also means any query
+   * requiring positional information, such as {@link
+   * org.apache.lucene.search.PhraseQuery} or {@link org.apache.lucene.search.spans.SpanQuery} subclasses will
+   * silently fail to find results.
+   */
+  public void setOmitTermFreqAndPositions(boolean omitTermFreqAndPositions) {
+    this.omitTermFreqAndPositions = omitTermFreqAndPositions;
+  }
+
+  /**
+   * Prints a Field for human consumption.
+   */
+  @Override
+  public final String toString() {
+    StringBuilder result = new StringBuilder();
+    if (isStored) {
+      result.append("stored");
+    }
+    if (isIndexed) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append("indexed");
+    }
+    if (isTokenized) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append("tokenized");
+    }
+    if (storeTermVector) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append("termVector");
+    }
+    if (storeOffsetWithTermVector) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append("termVectorOffsets");
+    }
+    if (storePositionWithTermVector) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append("termVectorPosition");
+    }
+    if (isBinary) {
+      if (result.length() > 0) {
+        result.append(",");
+      }
+      result.append("binary");
+    }
+    if (omitNorms) {
+      result.append(",omitNorms");
+    }
+    if (omitTermFreqAndPositions) {
+      result.append(",omitTermFreqAndPositions");
+    }
+    if (lazy) {
+      result.append(",lazy");
+    }
+    result.append('<').append(name).append(':');
+
+    if (fieldsData != null && !lazy) {
+      result.append(fieldsData);
+    }
+
+    result.append('>');
+    return result.toString();
+  }
+}
Index: src/java/org/apache/lucene/document/NumericField.java
===================================================================
--- src/java/org/apache/lucene/document/NumericField.java	(revision 917019)
+++ src/java/org/apache/lucene/document/NumericField.java	Sat Mar 13 18:07:23 CET 2010
@@ -138,7 +138,7 @@
  *
  * @since 2.9
  */
-public final class NumericField extends AbstractField {
+public final class NumericField extends Field {
 
   private final NumericTokenStream numericTS;
 
@@ -193,7 +193,7 @@
    * @param index if the field should be indexed using {@link NumericTokenStream}
    */
   public NumericField(String name, int precisionStep, Field.Store store, boolean index) {
-    super(name, store, index ? Field.Index.ANALYZED_NO_NORMS : Field.Index.NO, Field.TermVector.NO);
+    super(name, null, store, index ? Field.Index.ANALYZED_NO_NORMS : Field.Index.NO, Field.TermVector.NO);
     setOmitTermFreqAndPositions(true);
     numericTS = new NumericTokenStream(precisionStep);
   }
@@ -272,4 +272,35 @@
     return this;
   }
 
+  /**
+   * An unsupported operation for NumericFields
+   */
+  @Override
+  public void setValue(String value) {
+    throw new UnsupportedOperationException("Cannot set String values in NumericField");
-}
+  }
+
+  /**
+   * An unsupported operation for NumericFields
+   */
+  @Override
+  public void setValue(Reader value) {
+    throw new UnsupportedOperationException("Cannot set value through Readers in NumericFields");
+  }
+
+  /**
+   * An unsupported operation for NumericFields
+   */
+  @Override
+  public void setValue(byte[] value) {
+    throw new UnsupportedOperationException("Cannot set byte[] values in NumericFields");
+  }
+
+  /**
+   * An unsupported operation for NumericFields
+   */
+  @Override
+  public void setValue(byte[] value, int offset, int length) {
+    throw new UnsupportedOperationException("Cannot set byte[] values in NumericFields");
+  }
+}
Index: src/java/org/apache/lucene/document/AbstractField.java
===================================================================
--- src/java/org/apache/lucene/document/AbstractField.java	(revision 917019)
+++ src/java/org/apache/lucene/document/AbstractField.java	Sat Mar 13 18:11:07 CET 2010
@@ -24,8 +24,11 @@
 
 /**
  *
- *
+ * @deprecated The functionality of this class has been moved to {@link Field}
+ *             therefore subclasses should extend {@link Field}.  This class
+ *             will be removed in a future version.
  **/
+@Deprecated
 public abstract class AbstractField implements Fieldable {
 
   protected String name = "body";
