/*
* Copyright 2008-2009 MOPAS(Ministry of Public Administration and Security).
*
* 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 egovframework.rte.itl.integration.type.support;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import egovframework.rte.itl.integration.metadata.RecordTypeDefinition;
import egovframework.rte.itl.integration.metadata.RecordTypeFieldDefinition;
import egovframework.rte.itl.integration.metadata.dao.RecordTypeDefinitionDao;
import egovframework.rte.itl.integration.type.CircularInheritanceException;
import egovframework.rte.itl.integration.type.ListType;
import egovframework.rte.itl.integration.type.NoSuchTypeException;
import egovframework.rte.itl.integration.type.PrimitiveType;
import egovframework.rte.itl.integration.type.RecordType;
import egovframework.rte.itl.integration.type.Type;
import egovframework.rte.itl.integration.type.TypeLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
/**
* 전자정부 연계 서비스의 표준 메시지의 Type을 읽어오기 위한 클래스
* <p>
* <b>NOTE:</b> 전자정부 연계 서비스의 표준 메시지의 Type을 읽어오기 위한 Class이다.
* </p>
*
* @author 실행환경 개발팀 심상호
* @since 2009.06.01
* @version 1.0
* @see <pre>
* == 개정이력(Modification Information) ==
*
* 수정일 수정자 수정내용
* ------- -------- ---------------------------
* 2009.06.01 심상호 최초 생성
*
* </pre>
*/
public class TypeLoaderUsingMetadata implements TypeLoader, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(TypeLoaderUsingMetadata.class);
/**
* <code>RecordTypeDefinition</code>를 읽어오기 위한 DAO 객체
*/
private RecordTypeDefinitionDao recordTypeDefinitionDao;
/** 기존에 load된 Type을 담고 있는 map */
private Map<String, Type> typePool = new HashMap<String, Type>();
/**
* Default Constructor
*/
public TypeLoaderUsingMetadata() {
super();
}
/**
* Constructor
*
* @param recordTypeDefinitionDao
*/
public TypeLoaderUsingMetadata(
final RecordTypeDefinitionDao recordTypeDefinitionDao) {
super();
this.recordTypeDefinitionDao = recordTypeDefinitionDao;
}
/**
* recordTypeDefinitionDao
*
* @param recordTypeDefinitionDao
* the recordTypeDefinitionDao to set
*/
public void setRecordTypeDefinitionDao(
RecordTypeDefinitionDao recordTypeDefinitionDao) {
this.recordTypeDefinitionDao = recordTypeDefinitionDao;
}
public void afterPropertiesSet() throws Exception {
if (recordTypeDefinitionDao == null) {
throw new IllegalArgumentException();
}
}
public Type getType(String id) {
return getType(id, null);
}
/**
* Type <code>id</code>에 해당하는 <code>Type</code> 객체를 읽어온다.
* <code>loadingTypes</code>는 현재 loading하고 있는 Type 객제로서 circular reference가
* 발생한 경우, 무한 loop에 빠지는 것을 막기 위한 arument이다.
*
* @param id
* type id
* @param loadingTypes
* loading중인 Types
* @return <code>Type</code> 객체
* @throws NoSuchTypeException
* <code>id</code>가 null이거나, 해당하는 Type이 정의되어 있지 않을 경우
* @throws CircularInheritanceException
* RecordType의 경우, 순환 상속이 발생한 경우
*/
private Type getType(String id, Map<String, Type> loadingTypes) {
LOGGER.debug("get Type(id = \"{}\")", id);
// id 값이 null인 경우, NoSuchTypeException을 던진다.
if (id == null) {
LOGGER.error("Argument 'id' is null");
throw new NoSuchTypeException();
}
Type type = null;
// 현재 loading중인 Type 중에서 찾는다.
if (loadingTypes != null) {
type = loadingTypes.get(id);
if (type != null) {
LOGGER.debug("Type(id = \"{}\") exists in load hierachy : {}",
id, type);
return type;
}
}
// Primitive Type 중에서 찾는다.
type = PrimitiveType.getPrimitiveType(id);
if (type != null) {
LOGGER.debug("Type(id = \"{}\") is a primitive type : {}", id, type);
return type;
}
// 기존의 load된 type 중에서 동일한 id의 Type을 검색한다.
type = typePool.get(id);
if (type != null) {
LOGGER.debug("Type(id = \"{}\") is already loaded : {}", id, type);
return type;
}
// [!!! 중요 !!!]
// List Type의 Element Type 또는 Record Type의
// Field Type이
// 현재 id의 Type일 수 있다. 이 경우, 무한 loop에 빠질 수 있으므로
// 미리 객체를 생성하여 loadingTypes에 추가한 후, 내부 정의는 나중에
// 추가한다.
LOGGER.debug("Load new type(id= \"{}\"", id);
if (loadingTypes == null) {
loadingTypes = new HashMap<String, Type>();
}
// List Type 인지 검사한다.
if (id.endsWith("[]")) {
LOGGER.debug("Type(id = \"{}\") is a list type", id);
// List Type (임시로 BOOLEAN Type을 Element로
// 갖도록 생성한다.)
type = new ListType(id, id, PrimitiveType.BOOLEAN); // 임시
loadingTypes.put(id, type);
Type elementType = getType(id.substring(0, id.length() - 2),
loadingTypes);
LOGGER.debug("ListType(id = \"{}\")'s elementType = {}", id,
elementType);
((ListType) type).setElementType(elementType);
loadingTypes.remove(id);
} else {
LOGGER.debug("Type(id = \"{}\") is a record type", id);
RecordTypeDefinition recordTypeDefinition = recordTypeDefinitionDao.getRecordTypeDefinition(id);
if (recordTypeDefinition == null) {
LOGGER.error("No Such RecordTypeDefinition(id = \"{}\"", id);
throw new NoSuchTypeException();
}
if (recordTypeDefinition.isValid() == false) {
LOGGER.error("RecordTypeDefinition(id = \"{}\" is invalid", id);
throw new NoSuchTypeException();
}
// Record Type
type = new RecordType(id, recordTypeDefinition.getName());
loadingTypes.put(id, type);
Map<String, RecordTypeDefinition> occurredTypes = new HashMap<String, RecordTypeDefinition>();
Map<String, Type> fieldTypes = new HashMap<String, Type>();
RecordTypeDefinition currentRecordTypeDefinition = recordTypeDefinition;
while (currentRecordTypeDefinition != null) {
if (occurredTypes.containsKey(currentRecordTypeDefinition.getId())) {
throw new CircularInheritanceException();
}
occurredTypes.put(currentRecordTypeDefinition.getId(),
currentRecordTypeDefinition);
for (Entry<String, RecordTypeFieldDefinition> entry : currentRecordTypeDefinition.getFields().entrySet()) {
if (fieldTypes.containsKey(entry.getKey()) == false) {
fieldTypes.put(
entry.getKey(),
getType(entry.getValue().getTypeId(),
loadingTypes));
}
}
currentRecordTypeDefinition = currentRecordTypeDefinition.getParent();
}
if (LOGGER.isDebugEnabled()) {
for (Entry<String, Type> entry : fieldTypes.entrySet()) {
LOGGER.debug(
"RecordType(id = \"{}\")'s field[{}] type = {}",
id, entry.getKey(), entry.getValue());
}
}
((RecordType) type).setFieldTypes(fieldTypes);
loadingTypes.remove(id);
}
typePool.put(id, type);
return type;
}
}