content: package com.alipay.rdf.file.protocol;
  
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.List;
  
  import org.w3c.dom.Element;
  import org.w3c.dom.Node;
  import org.w3c.dom.NodeList;
  
  import com.alipay.rdf.file.exception.RdfErrorEnum;
  import com.alipay.rdf.file.exception.RdfFileException;
  import com.alipay.rdf.file.function.ColumnFunctionWrapper;
  import com.alipay.rdf.file.function.RdfFunction;
  import com.alipay.rdf.file.loader.ExtensionLoader;
  import com.alipay.rdf.file.meta.FileColumnMeta;
  import com.alipay.rdf.file.meta.FileColumnRangeMeta;
  import com.alipay.rdf.file.meta.FileColumnTypeMeta;
  import com.alipay.rdf.file.model.FileDataTypeEnum;
  import com.alipay.rdf.file.spi.RdfFileFunctionSpi;
  import com.alipay.rdf.file.spi.RdfFileRowSplitSpi;
  import com.alipay.rdf.file.util.RdfFileConstants;
  import com.alipay.rdf.file.util.RdfFileUtil;
  
  /**
   * Copyright (C) 2013-2018 Ant Financial Services Group
   * 
   * @author hongwei.quhw
   * @version $Id: XmlFileDefinitionParser.java, v 0.1 2017年4月1日 上午11:36:16 hongwei.quhw Exp $
   */
  public class FileDefinitionParser {
      private NodeletParser      parser = new NodeletParser();
  
      private ProtocolDefinition pd     = new ProtocolDefinition();
  
      public FileDefinitionParser() {
          parser.addNodelet("/protocol", new Nodelet() {
              @Override
              public void process(String xpath, Element ele) {
                  String protocolName = RdfFileUtil.assertTrimNotBlank(ele.getAttribute("name"));
                  pd.setName(protocolName);
  
                  String rowSplitRef = RdfFileConstants.DEFAULT_ROW_SPLIT;
                  if (RdfFileUtil.isNotBlank(ele.getAttribute("rowsplit"))) {
                      rowSplitRef = RdfFileUtil.assertTrimNotBlank(ele.getAttribute("rowsplit"));
                  }
                  RdfFileRowSplitSpi rowSplit = ExtensionLoader
                      .getExtensionLoader(RdfFileRowSplitSpi.class).getExtension(rowSplitRef);
                  RdfFileUtil.assertNotNull(rowSplit,
                      "文件protocol=" + protocolName + "中定义的rowsplit=" + rowSplitRef + "找不到对应的服务实现");
                  pd.setRowSplit(rowSplit);
              }
          });
  
          parser.addNodelet("/protocol/head/row", new Nodelet() {
              @Override
              public void process(String xpath, Element node) {
                  parseRow(xpath, node, pd.getHeads(), FileDataTypeEnum.HEAD);
              }
          });
  
          parser.addNodelet("/protocol/body/row", new Nodelet() {
              @Override
              public void process(String xpath, Element node) {
                  parseRow(xpath, node, pd.getBodys(), FileDataTypeEnum.BODY);
              }
          });
  
          parser.addNodelet("/protocol/tail/row", new Nodelet() {
              @Override
              public void process(String xpath, Element node) {
                  parseRow(xpath, node, pd.getTails(), FileDataTypeEnum.TAIL);
              }
          });
  
          parser.addNodelet("/protocol/head/row/column", new Nodelet() {
              @Override
              public void process(String xpath, Element node) {
                  parseRowColumn(xpath, node, pd.getHeads(), FileDataTypeEnum.HEAD);
              }
          });
  
          parser.addNodelet("/protocol/body/row/column", new Nodelet() {
              @Override
              public void process(String xpath, Element node) {
                  parseRowColumn(xpath, node, pd.getBodys(), FileDataTypeEnum.BODY);
              }
          });
  
          parser.addNodelet("/protocol/tail/row/column", new Nodelet() {
              @Override
              public void process(String xpath, Element node) {
                  parseRowColumn(xpath, node, pd.getTails(), FileDataTypeEnum.TAIL);
              }
          });
      }
  
      private void parseRow(String xpath, Element node, List<RowDefinition> rowDefinitions,
                            FileDataTypeEnum rowType) {
          String output = node.getAttribute("output");
          RowDefinition rd = new RowDefinition();
          rowDefinitions.add(rd);
  
          if (RdfFileUtil.isNotBlank(output)) {
              rd.setOutput(RdfFunction.parse(output.trim(), rowType));
          } else {
              String columnloop = node.getAttribute("columnloop");
              if (RdfFileUtil.isNotBlank(columnloop)) {
                  rd.setColumnloop(Boolean.parseBoolean(columnloop.trim()));
              } else {
                  rd.setColumnloop(RdfFileConstants.DEFAULT_COLUMN_LOOP);
              }
  
              String columnLayout = node.getAttribute("columnLayout");
              if (RdfFileUtil.isNotBlank(columnLayout)) {
                  rd.setColumnLayout(ColumnLayoutEnum.getColumnLayoutByName(columnLayout.trim()));
              } else {
                  rd.setColumnLayout(RdfFileConstants.DEFAULT_COLUMN_LAYOUT);
              }
          }
  
          String type = RdfFileUtil.trimNotNull(node.getAttribute("type"));
          if (RdfFileUtil.isNotBlank(type)) {
              FileColumnTypeMeta columnTypeMeta = null;
              FileColumnRangeMeta columnRangeMeta = null;
              String[] fileds = type.split("\\|");
              for (int i = 0; i < fileds.length; i++) {
                  String field = fileds[i];
                  int semicolonIndex = field.indexOf(":");
                  String extra = null;
                  if (semicolonIndex > 0) {
                      extra = field.substring(semicolonIndex + 1);
                      field = field.substring(0, semicolonIndex);
                  }
  
                  FileColumnTypeMeta typeMeta = FileColumnTypeMeta.tryValueOf(field, extra);
                  if (null != typeMeta) {
                      columnTypeMeta = typeMeta;
                  }
                  FileColumnRangeMeta rangeMeta = FileColumnRangeMeta.tryValueOf(field, extra);
                  if (null != rangeMeta) {
                      columnRangeMeta = rangeMeta;
                  }
              }
  
              FileColumnMeta columnMeta = new FileColumnMeta(-1, output, output, columnTypeMeta,
                  false, columnRangeMeta, null, null, rowType);
              rd.setColumnMeta(columnMeta);
          }
      }
  
      private void parseRowColumn(String xpath, Element node, List<RowDefinition> rowDefinitions,
                                  FileDataTypeEnum rowType) {
          NodeList nl = node.getChildNodes();
          List<RdfFileFunctionSpi> rfs = new ArrayList<RdfFileFunctionSpi>();
          for (int i = 0; i < nl.getLength(); i++) {
              Node nd = nl.item(i);
              if (!(nd instanceof Element)) {
                  continue;
              }
  
              Element outputEle = (Element) nd;
              RdfFileUtil.assertEquals(outputEle.getTagName(), "output");
  
              rfs.add(RdfFunction.parse(RdfFileUtil.assertTrimNotBlank(outputEle.getTextContent()),
                  rowType));
          }
  
          RowDefinition rd = rowDefinitions.get(rowDefinitions.size() - 1);
          if (rfs.size() > 0) {
              rd.setOutput(new ColumnFunctionWrapper(rfs, rowType));
          } else {
              throw new RdfFileException(xpath + " 下面没有配置output节点",
                  RdfErrorEnum.PROTOCOL_DEFINE_ERROR);
          }
      }
  
      public ProtocolDefinition parseFileDefinition(InputStream is) {
          RdfFileUtil.assertNotNull(is, "XmlFileDefinitionParser InputStream 为空");
  
          parser.parse(is);
  
          return pd;
      }
  
  }
  