gt:       finfo.add( fieldable.name(), f );
context: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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.
   */
  
  package org.apache.solr.handler.admin;
  
  import java.io.IOException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import java.util.*;
  
  import org.apache.commons.lang.BooleanUtils;
  import org.apache.lucene.index.*;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import org.apache.lucene.analysis.Analyzer;
  import org.apache.lucene.document.Document;
  import org.apache.lucene.document.Fieldable;
  import org.apache.lucene.index.FieldInfo.IndexOptions;
  import static org.apache.lucene.index.FieldInfo.IndexOptions.DOCS_ONLY;
  import static org.apache.lucene.index.FieldInfo.IndexOptions.DOCS_AND_FREQS;
  
  import org.apache.lucene.search.Query;
  import org.apache.lucene.search.TermRangeQuery;
  import org.apache.lucene.search.TopDocs;
  import org.apache.lucene.store.Directory;
  import org.apache.lucene.util.PriorityQueue;
  import org.apache.solr.analysis.CharFilterFactory;
  import org.apache.solr.analysis.TokenFilterFactory;
  import org.apache.solr.analysis.TokenizerChain;
  import org.apache.solr.analysis.TokenizerFactory;
  import org.apache.solr.common.SolrException;
  import org.apache.solr.common.luke.FieldFlag;
  import org.apache.solr.common.params.CommonParams;
  import org.apache.solr.common.params.SolrParams;
  import org.apache.solr.common.util.NamedList;
  import org.apache.solr.common.util.SimpleOrderedMap;
  import org.apache.solr.common.util.Base64;
  import org.apache.solr.handler.RequestHandlerBase;
  import org.apache.solr.request.SolrQueryRequest;
  import org.apache.solr.response.SolrQueryResponse;
  import org.apache.solr.schema.FieldType;
  import org.apache.solr.schema.IndexSchema;
  import org.apache.solr.schema.SchemaField;
  import org.apache.solr.search.SolrIndexSearcher;
  
  /**
   * This handler exposes the internal lucene index.  It is inspired by and 
   * modeled on Luke, the Lucene Index Browser by Andrzej Bialecki.
   *   http://www.getopt.org/luke/
   * <p>
   * NOTE: the response format is still likely to change.  It should be designed so
   * that it works nicely with an XSLT transformation.  Until we have a nice
   * XSLT front end for /admin, the format is still open to change.
   * </p>
   * 
   * For more documentation see:
   *  http://wiki.apache.org/solr/LukeRequestHandler
   * 
   * @version $Id: LukeRequestHandler.java 1242152 2012-02-08 22:31:07Z erick $
   * @since solr 1.2
   */
  public class LukeRequestHandler extends RequestHandlerBase 
  {
    private static Logger log = LoggerFactory.getLogger(LukeRequestHandler.class);
    
    public static final String NUMTERMS = "numTerms";
    public static final String DOC_ID = "docId";
    public static final String ID = "id";
    public static final String REPORT_DOC_COUNT = "reportDocCount";
    public static final int DEFAULT_COUNT = 10;
  
    static final int HIST_ARRAY_SIZE = 33;
    
    @Override
    public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
    {    
      IndexSchema schema = req.getSchema();
      SolrIndexSearcher searcher = req.getSearcher();
      IndexReader reader = searcher.getReader();
      SolrParams params = req.getParams();
      int numTerms = params.getInt( NUMTERMS, DEFAULT_COUNT );
  
      // Always show the core lucene info
  
      Map<String, TopTermQueue> topTerms = new TreeMap<String, TopTermQueue>();
  
      // If no doc is given, show all fields and top terms
      Set<String> fields = null;
      String fl = params.get(CommonParams.FL);
      if (fl != null) {
        fields = new TreeSet<String>(Arrays.asList(fl.split( "[,\\s]+" )));
      }
  
      if ( "schema".equals( params.get( "show" ))) {
        numTerms = 0; // Abort any statistics gathering.
      }
      rsp.add("index", getIndexInfo(reader, numTerms, topTerms, fields ) );
  
      Integer docId = params.getInt( DOC_ID );
      if( docId == null && params.get( ID ) != null ) {
        // Look for something with a given solr ID
        SchemaField uniqueKey = schema.getUniqueKeyField();
        String v = uniqueKey.getType().toInternal( params.get(ID) );
        Term t = new Term( uniqueKey.getName(), v );
        docId = searcher.getFirstMatch( t );
        if( docId < 0 ) {
          throw new SolrException( SolrException.ErrorCode.NOT_FOUND, "Can't find document: "+params.get( ID ) );
        }
      }
      // Read the document from the index
      if( docId != null ) {
        Document doc = null;
        try {
          doc = reader.document( docId );
        }
        catch( Exception ex ) {}
        if( doc == null ) {
          throw new SolrException( SolrException.ErrorCode.NOT_FOUND, "Can't find document: "+docId );
        }
        
        SimpleOrderedMap<Object> info = getDocumentFieldsInfo( doc, docId, reader, schema );
        
        SimpleOrderedMap<Object> docinfo = new SimpleOrderedMap<Object>();
        docinfo.add( "docId", docId );
        docinfo.add( "lucene", info );
        docinfo.add( "solr", doc );
        rsp.add( "doc", docinfo );
      }
      else if ( "schema".equals( params.get( "show" ) ) ) {
        rsp.add( "schema", getSchemaInfo( req.getSchema() ) );
      }
      else {
        boolean reportDocCount = true;
        String reportDoc = params.get(REPORT_DOC_COUNT, "false");
        if ("1".equals(reportDoc)) reportDocCount = true;
        else if ("0".equals(reportDoc)) reportDocCount = false;
        else reportDocCount = BooleanUtils.toBoolean(reportDoc);
        rsp.add( "fields", getIndexedFieldsInfo( searcher, fields, numTerms, topTerms, reportDocCount ) ) ;
      }
  
      // Add some generally helpful information
      NamedList<Object> info = new SimpleOrderedMap<Object>();
      info.add( "key", getFieldFlagsKey() );
      info.add( "NOTE", "Document Frequency (df) is not updated when a document is marked for deletion.  df values include deleted documents." );
      rsp.add( "info", info );
      rsp.setHttpCaching(false);
    }
  
    
  
    /**
     * @return a string representing a Fieldable's flags.  
     */
    private static String getFieldFlags( Fieldable f )
    {
      IndexOptions opts = (f == null) ? null : f.getIndexOptions();
  
      StringBuilder flags = new StringBuilder();
  
      flags.append( (f != null && f.isIndexed())                     ? FieldFlag.INDEXED.getAbbreviation() : '-' );
      flags.append( (f != null && f.isTokenized())                   ? FieldFlag.TOKENIZED.getAbbreviation() : '-' );
      flags.append( (f != null && f.isStored())                      ? FieldFlag.STORED.getAbbreviation() : '-' );
      flags.append( (false)                                          ? FieldFlag.MULTI_VALUED.getAbbreviation() : '-' ); // SchemaField Specific
  
      flags.append( (f != null && f.isTermVectorStored())            ? FieldFlag.TERM_VECTOR_STORED.getAbbreviation() : '-' );
      flags.append( (f != null && f.isStoreOffsetWithTermVector())   ? FieldFlag.TERM_VECTOR_OFFSET.getAbbreviation() : '-' );
      flags.append( (f != null && f.isStorePositionWithTermVector()) ? FieldFlag.TERM_VECTOR_POSITION.getAbbreviation() : '-' );
      flags.append( (f != null && f.getOmitNorms())                  ? FieldFlag.OMIT_NORMS.getAbbreviation() : '-' );
  
      flags.append( (f != null && DOCS_ONLY == opts ) ? 
                    FieldFlag.OMIT_TF.getAbbreviation() : '-' );
      
      flags.append( (f != null && DOCS_AND_FREQS == opts ) ?
                    FieldFlag.OMIT_POSITIONS.getAbbreviation() : '-' );
  
      flags.append( (f != null && f.isLazy())                        ? FieldFlag.LAZY.getAbbreviation() : '-' );
      flags.append( (f != null && f.isBinary())                      ? FieldFlag.BINARY.getAbbreviation() : '-' );
      flags.append( (false)                                          ? FieldFlag.SORT_MISSING_FIRST.getAbbreviation() : '-' ); // SchemaField Specific
      flags.append( (false)                                          ? FieldFlag.SORT_MISSING_LAST.getAbbreviation() : '-' ); // SchemaField Specific
      return flags.toString();
    }
    
    /**
     * @return a string representing a SchemaField's flags.  
     */
    private static String getFieldFlags( SchemaField f )
    {
      FieldType t = (f==null) ? null : f.getType();
      
      // see: http://www.nabble.com/schema-field-properties-tf3437753.html#a9585549
      boolean lazy = false; // "lazy" is purely a property of reading fields
      boolean binary = false; // Currently not possible
      
      StringBuilder flags = new StringBuilder();
      flags.append( (f != null && f.indexed())             ? FieldFlag.INDEXED.getAbbreviation() : '-' );
      flags.append( (t != null && t.isTokenized())         ? FieldFlag.TOKENIZED.getAbbreviation() : '-' );
      flags.append( (f != null && f.stored())              ? FieldFlag.STORED.getAbbreviation() : '-' );
      flags.append( (f != null && f.multiValued())         ? FieldFlag.MULTI_VALUED.getAbbreviation() : '-' );
      flags.append( (f != null && f.storeTermVector() )    ? FieldFlag.TERM_VECTOR_STORED.getAbbreviation() : '-' );
      flags.append( (f != null && f.storeTermOffsets() )   ? FieldFlag.TERM_VECTOR_OFFSET.getAbbreviation() : '-' );
      flags.append( (f != null && f.storeTermPositions() ) ? FieldFlag.TERM_VECTOR_POSITION.getAbbreviation() : '-' );
      flags.append( (f != null && f.omitNorms())           ? FieldFlag.OMIT_NORMS.getAbbreviation() : '-' );
      flags.append( (f != null && 
                     f.omitTermFreqAndPositions() )        ? FieldFlag.OMIT_TF.getAbbreviation() : '-' );
      flags.append( (f != null && f.omitPositions() )      ? FieldFlag.OMIT_POSITIONS.getAbbreviation() : '-' );
      flags.append( (lazy)                                 ? FieldFlag.LAZY.getAbbreviation() : '-' );
      flags.append( (binary)                               ? FieldFlag.BINARY.getAbbreviation() : '-' );
      flags.append( (f != null && f.sortMissingFirst() )   ? FieldFlag.SORT_MISSING_FIRST.getAbbreviation() : '-' );
      flags.append( (f != null && f.sortMissingLast() )    ? FieldFlag.SORT_MISSING_LAST.getAbbreviation() : '-' );
      return flags.toString();
    }
    
    /**
     * @return a key to what each character means
     */
    public static SimpleOrderedMap<String> getFieldFlagsKey() {
      SimpleOrderedMap<String> key = new SimpleOrderedMap<String>();
      for (FieldFlag f : FieldFlag.values()) {
        key.add(String.valueOf(f.getAbbreviation()), f.getDisplay() );
      }
      return key;
    }
    
    private static SimpleOrderedMap<Object> getDocumentFieldsInfo( Document doc, int docId, IndexReader reader, IndexSchema schema ) throws IOException
    { 
      SimpleOrderedMap<Object> finfo = new SimpleOrderedMap<Object>();
      for( Object o : doc.getFields() ) {
        Fieldable fieldable = (Fieldable)o;
        SimpleOrderedMap<Object> f = new SimpleOrderedMap<Object>();
        
        SchemaField sfield = schema.getFieldOrNull( fieldable.name() );
        FieldType ftype = (sfield==null)?null:sfield.getType();
  
        f.add( "type", (ftype==null)?null:ftype.getTypeName() );
        f.add( "schema", getFieldFlags( sfield ) );
        f.add( "flags", getFieldFlags( fieldable ) );
  
        Term t = new Term(fieldable.name(), ftype!=null ? ftype.storedToIndexed(fieldable) : fieldable.stringValue());
  
        f.add( "value", (ftype==null)?null:ftype.toExternal( fieldable ) );
  
        // TODO: this really should be "stored"
        f.add( "internal", fieldable.stringValue() );  // may be a binary number
  
        byte[] arr = fieldable.getBinaryValue();
        if (arr != null) {
          f.add( "binary", Base64.byteArrayToBase64(arr, 0, arr.length));
        }
        f.add( "boost", fieldable.getBoost() );
        f.add( "docFreq", t.text()==null ? 0 : reader.docFreq( t ) ); // this can be 0 for non-indexed fields
              
        // If we have a term vector, return that
        if( fieldable.isTermVectorStored() ) {
          try {
            TermFreqVector v = reader.getTermFreqVector( docId, fieldable.name() );
            if( v != null ) {
              SimpleOrderedMap<Integer> tfv = new SimpleOrderedMap<Integer>();
              for( int i=0; i<v.size(); i++ ) {
                tfv.add( v.getTerms()[i], v.getTermFrequencies()[i] );
              }
              f.add( "termVector", tfv );
            }
          }
          catch( Exception ex ) {
            log.warn( "error writing term vector", ex );
          }
        }
        
  