content: /**
   * 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.response;
  
  import org.apache.lucene.document.Document;
  import org.apache.solr.common.util.NamedList;
  import org.apache.solr.common.util.FastWriter;
  import org.apache.solr.common.SolrDocument;
  import org.apache.solr.common.SolrDocumentList;
  import org.apache.solr.request.SolrQueryRequest;
  import org.apache.solr.schema.IndexSchema;
  import org.apache.solr.schema.DateField;
  import org.apache.solr.search.DocList;
  import java.io.IOException;
  import java.io.Writer;
  import java.util.Date;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.Set;
  
  /** Base class for text-oriented response writers.
   *
   * @version $Id$
   */
  public abstract class TextResponseWriter {
    
    protected final FastWriter writer;
    protected final IndexSchema schema;
    protected final SolrQueryRequest req;
    protected final SolrQueryResponse rsp;
  
    // the default set of fields to return for each document
    protected Set<String> returnFields;
  
    protected int level;
    protected boolean doIndent;
  
  
    public TextResponseWriter(Writer writer, SolrQueryRequest req, SolrQueryResponse rsp) {
      this.writer = FastWriter.wrap(writer);
      this.schema = req.getSchema();
      this.req = req;
      this.rsp = rsp;
      String indent = req.getParams().get("indent");
      if (indent != null && !"".equals(indent) && !"off".equals(indent)) {
        doIndent=true;
      }
      returnFields = rsp.getReturnFields();
    }
  
    /** done with this ResponseWriter... make sure any buffers are flushed to writer */
    public void close() throws IOException {
      writer.flushBuffer();
    }
  
    /** returns the Writer that the response is being written to */
    public Writer getWriter() { return writer; }
  
  
    public void indent() throws IOException {
       if (doIndent) indent(level);
    }
  
    public void indent(int lev) throws IOException {
      writer.write(XMLWriter.indentChars, 0, Math.min((lev<<1)+1, XMLWriter.indentChars.length));
    }
  
  
    //
    // Functions to manipulate the current logical nesting level.
    // Any indentation will be partially based on level.
    //
    public void setLevel(int level) { this.level = level; }
    public int level() { return level; }
    public int incLevel() { return ++level; }
    public int decLevel() { return --level; }
    public void setIndent(boolean doIndent) {
      this.doIndent = doIndent;
    }
  
  
    public abstract void writeNamedList(String name, NamedList val) throws IOException;
  
    public void writeVal(String name, Object val) throws IOException {
  
      // if there get to be enough types, perhaps hashing on the type
      // to get a handler might be faster (but types must be exact to do that...)
  
      // go in order of most common to least common
      if (val==null) {
        writeNull(name);
      } else if (val instanceof String) {
        writeStr(name, val.toString(), true);
        // micro-optimization... using toString() avoids a cast first
      } else if (val instanceof Integer) {
        writeInt(name, val.toString());
      } else if (val instanceof Boolean) {
        writeBool(name, val.toString());
      } else if (val instanceof Long) {
        writeLong(name, val.toString());
      } else if (val instanceof Date) {
        writeDate(name,(Date)val);
      } else if (val instanceof Float) {
        // we pass the float instead of using toString() because
        // it may need special formatting. same for double.
        writeFloat(name, ((Float)val).floatValue());
      } else if (val instanceof Double) {
        writeDouble(name, ((Double)val).doubleValue());
      } else if (val instanceof Document) {
        writeDoc(name, (Document)val, returnFields, 0.0f, false);
      } else if (val instanceof SolrDocument) {
        writeSolrDocument(name, (SolrDocument)val, returnFields, null);
      } else if (val instanceof DocList) {
        // requires access to IndexReader
        writeDocList(name, (DocList)val, returnFields,null);
      // }
      // else if (val instanceof DocSet) {
      // how do we know what fields to read?
      // todo: have a DocList/DocSet wrapper that
      // restricts the fields to write...?
      } else if (val instanceof SolrDocumentList) {
        writeSolrDocumentList(name, (SolrDocumentList)val, returnFields, null);
      } else if (val instanceof Map) {
        writeMap(name, (Map)val, false, true);
      } else if (val instanceof NamedList) {
        writeNamedList(name, (NamedList)val);
      } else if (val instanceof Iterable) {
        writeArray(name,((Iterable)val).iterator());
      } else if (val instanceof Object[]) {
        writeArray(name,(Object[])val);
      } else if (val instanceof Iterator) {
        writeArray(name,(Iterator)val);
      } else {
        // default... for debugging only
        writeStr(name, val.getClass().getName() + ':' + val.toString(), true);
      }
    }
  
    // names are passed when writing primitives like writeInt to allow many different
    // types of formats, including those where the name may come after the value (like
    // some XML formats).
  
    public abstract void writeDoc(String name, Document doc, Set<String> returnFields, float score, boolean includeScore) throws IOException;
  
    /**
     * @since solr 1.3
     */
    public abstract void writeSolrDocument(String name, SolrDocument doc, Set<String> returnFields, Map pseudoFields) throws IOException;  
  
    public abstract void writeDocList(String name, DocList ids, Set<String> fields, Map otherFields) throws IOException;
  
    /**
     * @since solr 1.3
     */
    public abstract void writeSolrDocumentList(String name, SolrDocumentList docs, Set<String> fields, Map otherFields) throws IOException;  
  
    public abstract void writeStr(String name, String val, boolean needsEscaping) throws IOException;
  
    public abstract void writeMap(String name, Map val, boolean excludeOuter, boolean isFirstVal) throws IOException;
  
    public abstract void writeArray(String name, Object[] val) throws IOException;
  
    public abstract void writeArray(String name, Iterator val) throws IOException;
  
    public abstract void writeNull(String name) throws IOException;
  
    /** if this form of the method is called, val is the Java string form of an int */
    public abstract void writeInt(String name, String val) throws IOException;
  
    public void writeInt(String name, int val) throws IOException {
      writeInt(name,Integer.toString(val));
    }
  
    /** if this form of the method is called, val is the Java string form of a long */
    public abstract void writeLong(String name, String val) throws IOException;
  
    public  void writeLong(String name, long val) throws IOException {
      writeLong(name,Long.toString(val));
    }
  
    /** if this form of the method is called, val is the Java string form of a boolean */
    public abstract void writeBool(String name, String val) throws IOException;
  
    public void writeBool(String name, boolean val) throws IOException {
      writeBool(name,Boolean.toString(val));
    }
  
    /** if this form of the method is called, val is the Java string form of a float */
    public abstract void writeFloat(String name, String val) throws IOException;
  
    public void writeFloat(String name, float val) throws IOException {
      String s = Float.toString(val);
      // If it's not a normal number, write the value as a string instead.
      // The following test also handles NaN since comparisons are always false.
      if (val > Float.NEGATIVE_INFINITY && val < Float.POSITIVE_INFINITY) {
        writeFloat(name,s);
      } else {
        writeStr(name,s,false);
      }
    }
  
    /** if this form of the method is called, val is the Java string form of a double */
    public abstract void writeDouble(String name, String val) throws IOException;
  
    public void writeDouble(String name, double val) throws IOException {
      String s = Double.toString(val);
      // If it's not a normal number, write the value as a string instead.
      // The following test also handles NaN since comparisons are always false.
      if (val > Double.NEGATIVE_INFINITY && val < Double.POSITIVE_INFINITY) {
        writeDouble(name,s);
      } else {
        writeStr(name,s,false);
      }
    }
  
    public void writeDate(String name, Date val) throws IOException {
      writeDate(name, DateField.formatExternal(val));
    }
  
    /** if this form of the method is called, val is the Solr ISO8601 based date format */
    public abstract void writeDate(String name, String val) throws IOException;
  
    public abstract void writeShort(String name, String val) throws IOException;
  
    public void writeShort(String name, short val) throws IOException{
      writeShort(name, Short.toString(val));
    }
  
    public abstract void writeByte(String name, String s) throws IOException;
  
    public void writeByte(String name, byte val) throws IOException{
      writeByte(name, Byte.toString(val));
    }
  }
  