public static String serialize(Expression expr, Map prefixMapping) {
    FastStringBuffer result = new FastStringBuffer(64);

    final Map<String, String> reversePrefixMapping = new HashMap<String, String>();
    for (Iterator<Map.Entry<String, String>> it = prefixMapping.entrySet().iterator();
        it.hasNext(); ) {
      Map.Entry<String, String> entry = it.next();
      reversePrefixMapping.put(entry.getValue(), entry.getKey());
    }

    serialize(result, expr, reversePrefixMapping);

    return result.toString();
  }
  public String getStringValue() {

    FastStringBuffer sb = new FastStringBuffer(16);

    sb.append("--");
    DateTimeValue.appendString(sb, calendar.get(Calendar.MONTH) + 1, 2);
    sb.append('-');
    DateTimeValue.appendString(sb, calendar.get(Calendar.DATE), 2);

    if (zoneSpecified) {
      DateTimeValue.appendTimezone(tzOffset, sb);
    }

    return sb.toString();
  }
  /**
   * Convert to string
   *
   * @return ISO 8601 representation. The value returned is the localized representation, that is it
   *     uses the timezone contained within the value itself.
   */
  public CharSequence getPrimitiveStringValue() {

    FastStringBuffer sb = new FastStringBuffer(30);
    int yr = year;
    if (year <= 0) {
      sb.append('-');
      yr = -yr + 1; // no year zero in lexical space
    }
    appendString(sb, yr, (yr > 9999 ? (yr + "").length() : 4));
    sb.append('-');
    appendTwoDigits(sb, month);
    sb.append('-');
    appendTwoDigits(sb, day);
    sb.append('T');
    appendTwoDigits(sb, hour);
    sb.append(':');
    appendTwoDigits(sb, minute);
    sb.append(':');
    appendTwoDigits(sb, second);
    if (microsecond != 0) {
      sb.append('.');
      int ms = microsecond;
      int div = 100000;
      while (ms > 0) {
        int d = ms / div;
        sb.append((char) (d + '0'));
        ms = ms % div;
        div /= 10;
      }
    }

    if (hasTimezone()) {
      appendTimezone(sb);
    }

    return sb;
  }
  private static String fixPreFixes(String xpath, Map<String, String> reversePrefixMapping) {
    FastStringBuffer result = new FastStringBuffer(xpath.length());

    Matcher m = Pattern.compile("\\{[^\\}]+\\}").matcher(xpath);

    int iLast = 0;
    while (m.find()) {
      result.append(xpath.substring(iLast, m.start()));
      final String match = m.group();
      result.append(reversePrefixMapping.get(match.substring(1, match.length() - 1)));
      result.append(':');
      iLast = m.end();
    }

    result.append(xpath.substring(iLast));
    return result.toString();
  }
  /**
   * @param result
   * @param expr
   */
  private static void serialize(
      FastStringBuffer result, Expression expr, Map reversePrefixMapping) {
    if (expr instanceof Assignation) {
      // XXX not yet supported
    } else if (expr instanceof AxisExpression) {
      AxisExpression axisExpression = (AxisExpression) expr;
      result.append(Axis.axisName[axisExpression.getAxis()]);
      result.append("::");

      final NodeTest nodeTest = axisExpression.getNodeTest();
      if (nodeTest == null) {
        result.append("node()");
      } else {
        result.append(fixPreFixes(nodeTest.toString(), reversePrefixMapping));
      }
    } else if (expr instanceof BinaryExpression) {
      BinaryExpression binaryExpression = (BinaryExpression) expr;
      result.append('(');
      serialize(result, binaryExpression.getOperands()[0], reversePrefixMapping);
      result.append(Token.tokens[binaryExpression.getOperator()]);
      serialize(result, binaryExpression.getOperands()[1], reversePrefixMapping);
      result.append(')');
    } else if (expr instanceof CompareToIntegerConstant) {
      CompareToIntegerConstant compareToIntegerConstant = (CompareToIntegerConstant) expr;
      result.append('(');
      serialize(result, compareToIntegerConstant.getOperand(), reversePrefixMapping);
      result.append(Token.tokens[compareToIntegerConstant.getComparisonOperator()]);
      result.append(Long.toString(compareToIntegerConstant.getComparand()));
      result.append(')');
    } else if (expr instanceof ConditionalSorter) {
      // XXX not yet supported
    } else if (expr instanceof ContextItemExpression) {
      result.append('.');
    } else if (expr instanceof ErrorExpression) {
      // Error do nothing
    } else if (expr instanceof FilterExpression) {
      FilterExpression filterExpression = (FilterExpression) expr;
      result.append('(');
      serialize(result, filterExpression.getControllingExpression(), reversePrefixMapping);
      result.append('[');
      serialize(result, filterExpression.getFilter(), reversePrefixMapping);
      result.append("])");

    } else if (expr instanceof FunctionCall) {
      FunctionCall functionCall = (FunctionCall) expr;
      StructuredQName name = functionCall.getFunctionName();
      if (name.getPrefix() != null && name.getPrefix().length() > 0) {
        result.append(name.getPrefix());
        result.append(":");
      }
      result.append(name.getLocalName());
      result.append("(");

      Iterator iter = functionCall.iterateSubExpressions();
      boolean first = true;
      while (iter.hasNext()) {
        result.append(first ? "" : ", ");
        SaxonXPathExpressionSerializer.serialize(
            result, (Expression) iter.next(), reversePrefixMapping);
        first = false;
      }

      result.append(")");
    } else if (expr instanceof Instruction) {
      // This is not an XPath expression
    } else if (expr instanceof IntegerRangeTest) {
      // XXX not yet supported
    } else if (expr instanceof IsLastExpression) {
      result.append("position() eq last()");
    } else if (expr instanceof Literal) {
      Literal literal = (Literal) expr;
      result.append(literal.getValue().toString());
    } else if (expr instanceof NumberInstruction) {
      // This is not an XPath expression
    } else if (expr instanceof PathExpression) {
      PathExpression pathExpression = (PathExpression) expr;
      result.append('(');
      serialize(result, pathExpression.getControllingExpression(), reversePrefixMapping);
      result.append('/');
      serialize(result, pathExpression.getControlledExpression(), reversePrefixMapping);
      result.append(')');
    } else if (expr instanceof PatternMatchExpression) {
      // XXX not yet supported
    } else if (expr instanceof PatternSponsor) {
      // XXX not yet supported
    } else if (expr instanceof SimpleContentConstructor) {
      // This is not an XPath expression
    } else if (expr instanceof SimpleExpression) {
      // This is not an XPath expression
    }
    /*
        else if (expr instanceof SimpleMappingExpression) {
    	    // XXX not yet supported
    	}
    */
    else if (expr instanceof ParentNodeExpression) {
      result.append("..");
    } else if (expr instanceof RootExpression) {
      // do nothing
    } else if (expr instanceof SortExpression) {
      // XXX not yet supported
    } else if (expr instanceof TailExpression) {
      // XXX not yet supported
    } else if (expr instanceof TupleExpression) {
      // This is not an XPath expression
    } else if (expr instanceof TupleSorter) {
      // This is not an XPath expression
    } else if (expr instanceof UnaryExpression) {
      UnaryExpression unaryExpression = (UnaryExpression) expr;
      serialize(
          result,
          unaryExpression.getBaseExpression(),
          reversePrefixMapping); // Not sure if this is correct in all cases
    } else if (expr instanceof VariableReference) {
      VariableReference variableReference = (VariableReference) expr;
      String d = variableReference.getDisplayName();
      result.append("$");
      result.append(d == null ? "$" : d);
    }
  }