/* * 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 org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; 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; /** * 전자정부 연계 서비스의 표준 메시지의 Type을 읽어오기 위한 클래스 * <p> * <b>NOTE:</b> 전자정부 연계 서비스의 표준 메시지의 Type을 읽어오기 위한 * Class이다. * @author 실행환경 개발팀 심상호 * @since 2009.06.01 * @version 1.0 * @see <pre> * == 개정이력(Modification Information) == * * 수정일 수정자 수정내용 * ------- -------- --------------------------- * 2009.06.01 심상호 최초 생성 * * </pre> */ public class TypeLoaderUsingMetadata implements TypeLoader, InitializingBean { private Log LOG = LogFactory.getLog(this.getClass()); /** * <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; } /** * @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) { LOG.debug("get Type(id = \"" + id + "\")"); // id 값이 null인 경우, NoSuchTypeException을 던진다. if (id == null) { LOG.error("Argument 'id' is null"); throw new NoSuchTypeException(); } Type type = null; // 현재 loading중인 Type 중에서 찾는다. if (loadingTypes != null) { type = loadingTypes.get(id); if (type != null) { LOG.debug("Type(id = \"" + id + "\") exists in load hierachy : " + type); return type; } } // Primitive Type 중에서 찾는다. type = PrimitiveType.getPrimitiveType(id); if (type != null) { LOG .debug("Type(id = \"" + id + "\") is a primitive type : " + type); return type; } // 기존의 load된 type 중에서 동일한 id의 Type을 검색한다. type = typePool.get(id); if (type != null) { LOG.debug("Type(id = \"" + id + "\") is already loaded : " + type); return type; } // [!!! 중요 !!!] // List Type의 Element Type 또는 Record Type의 // Field Type이 // 현재 id의 Type일 수 있다. 이 경우, 무한 loop에 빠질 수 있으므로 // 미리 객체를 생성하여 loadingTypes에 추가한 후, 내부 정의는 나중에 // 추가한다. LOG.debug("Load new type(id= \"" + id + "\""); if (loadingTypes == null) { loadingTypes = new HashMap<String, Type>(); } // List Type 인지 검사한다. if (id.endsWith("[]")) { LOG.debug("Type(id = \"" + id + "\") is a list type"); // 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); LOG.debug("ListType(id = \"" + id + "\")'s elementType = " + elementType); ((ListType) type).setElementType(elementType); loadingTypes.remove(id); } else { LOG.debug("Type(id = \"" + id + "\") is a record type"); RecordTypeDefinition recordTypeDefinition = recordTypeDefinitionDao.getRecordTypeDefinition(id); if (recordTypeDefinition == null) { LOG.error("No Such RecordTypeDefinition(id = \"" + id + "\""); throw new NoSuchTypeException(); } if (recordTypeDefinition.isValid() == false) { LOG .error("RecordTypeDefinition(id = \"" + id + "\" is invalid"); 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 (LOG.isDebugEnabled()) { for (Entry<String, Type> entry : fieldTypes.entrySet()) { LOG.debug("RecordType(id = \"" + id + "\")'s field[" + entry.getKey() + "] type = " + entry.getValue()); } } ((RecordType) type).setFieldTypes(fieldTypes); loadingTypes.remove(id); } typePool.put(id, type); return type; } }