/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed 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 com.alibaba.citrus.springext.impl;
import static com.alibaba.citrus.springext.support.SchemaUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedList;
import com.alibaba.citrus.springext.ConfigurationPointException;
import com.alibaba.citrus.springext.Schema;
import com.alibaba.citrus.util.io.ByteArrayInputStream;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamSource;
/**
* 这是schema的基类,实现了schema生命周期中的几个步骤:
* <ol>
* <li>Parse - 将originalSource读出来,生成document文档。这一步可以被跳过,例如configuration point schema文档是直接在内存中创建的,所以不经过这一步。</li>
* <li>Transform - 将document文档进行转换、修改。这一步可进行多次,且真正的转换会被延迟到要用到时,才进行。</li>
* <li>Analyze - 分析document的内容,取得其中的信息。例如取得targetNamespace等。</li>
* <li>Serialize - 将document转换回二进制流,以供外部程序读取。例如,供给schema exporter生成schema文件,或者供给resolver来验证SpringExt配置文件。
* 每一次transform以后,需要重新进行serialize操作,以便反映最新的修改。</li>
* </ol>
*
* @author Michael Zhou
*/
public abstract class SchemaBase implements Schema {
protected final static Logger log = LoggerFactory.getLogger(Schema.class);
private final InputStreamSource originalSource;
private Document document;
private boolean parsed;
private final LinkedList<Transformer> transformersQueue = createLinkedList();
private byte[] transformedData;
private boolean everTransformed;
private boolean analyzed;
public SchemaBase(InputStreamSource originalSource) {
this(originalSource, null, true);
}
public SchemaBase(Document originalDocument) {
this(null, originalDocument, false);
}
protected SchemaBase(InputStreamSource originalSource, Document originalDocument, boolean isInputStreamSource) {
if (isInputStreamSource) {
this.originalSource = assertNotNull(originalSource, "no InputStreamSource provided");
} else {
this.originalSource = null;
this.document = assertNotNull(originalDocument, "no Document provided");
this.parsed = true;
}
}
public final void transform(Transformer transformer) {
transform(transformer, false);
}
public final void transform(Transformer transformer, boolean doNow) {
transformersQueue.addLast(assertNotNull(transformer, "no Transformer provided"));
if (doNow) {
transform();
}
}
public final Document getDocument() {
parse();
transform();
return document;
}
public final InputStream getInputStream() {
serialize();
if (transformedData != null) {
return new ByteArrayInputStream(transformedData);
}
if (originalSource != null) {
return getOriginalInputStream();
} else {
fail("Found a BUG: OriginalSource can't be null here");
return null;
}
}
private void serialize() {
transform();
if (transformedData == null && document != null && (everTransformed || originalSource == null)) {
transformedData = getDocumentContent(document);
}
}
private void transform() {
if (!transformersQueue.isEmpty()) {
parse();
transformedData = null;
everTransformed = true;
while (!transformersQueue.isEmpty()) {
Transformer transformer = transformersQueue.removeFirst();
// 有可能因格式非法,不能生成document文件(document==null)。忽略这种情况。
if (document != null) {
transformer.transform(document, getName());
}
}
}
}
private void parse() {
if (!parsed) {
parsed = true;
try {
document = readDocument(getOriginalInputStream(), getName(), true);
} catch (DocumentException e) {
log.warn("Not a valid XML doc: {}, source={},\n{}", new Object[] { getName(), originalSource, e.getMessage() });
document = null;
}
}
}
private InputStream getOriginalInputStream() {
try {
return originalSource.getInputStream();
} catch (IOException e) {
throw new ConfigurationPointException("Failed to read text of schema file: " + getName() + ", source=" + originalSource, e);
}
}
protected final void analyze() {
if (!analyzed) {
analyzed = true;
parse();
transform();
if (document != null) {
doAnalyze();
}
}
}
protected abstract void doAnalyze();
@Override
public String toString() {
if (originalSource != null) {
return originalSource.toString();
} else {
return "generated-content";
}
}
}