/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2010, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.gml2; import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.Iterator; import org.eclipse.emf.common.notify.Adapter; import org.eclipse.emf.common.notify.Notification; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDFactory; import org.eclipse.xsd.XSDPackage; import org.geotools.util.Utilities; /** * Adapter for preventing memory leaks created by substitution group affiliations. * <p> * When an application schema contains an element in the gml:_Feature substitution group a link * from gml:_Feature back to the app schema element is created. Since the gml schema (and thus the * gml:_Feature) element is a singleton this creates a memory leak. This adapter watches the * {@link XSDElementDeclaration#getSubstitutionGroup()} of the gml:_Feature element and prevents it * from growing in size by making it a unique list of {@link XSDElementDeclaration} based on * qualified name. * </p> * * @author Justin Deoliveira, OpenGeo * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-gml2/src/main/java/org/geotools/gml2/SubstitutionGroupLeakPreventer.java $ */ public class SubstitutionGroupLeakPreventer implements Adapter { XSDElementDeclaration target; public Notifier getTarget() { return target; } public void setTarget(Notifier newTarget) { target = (XSDElementDeclaration) newTarget; } public boolean isAdapterForType(Object type) { return type instanceof XSDElementDeclaration; } public void notifyChanged(Notification notification) { int featureId = notification.getFeatureID(target.getClass()); if (featureId != XSDPackage.XSD_ELEMENT_DECLARATION__SUBSTITUTION_GROUP) { return; } if (notification.getEventType() != Notification.ADD) { return; } if (!(notification.getNewValue() instanceof XSDElementDeclaration)) { return; } XSDElementDeclaration el = (XSDElementDeclaration) notification.getNewValue(); XSDElementDeclaration e = target; while(e != null) { synchronized(e) { ArrayList<Integer> toremove = new ArrayList(); for (int i = 0; i < e.getSubstitutionGroup().size(); i++) { XSDElementDeclaration se = (XSDElementDeclaration) e.getSubstitutionGroup().get(i); if (se == null || (Utilities.equals(el.getTargetNamespace(), se.getTargetNamespace()) && Utilities.equals(el.getName(), se.getName()))) { toremove.add(i); } } //iterate back in reverse order and skip the last element as to keep the latest // version of the element ArrayList<XSDElementDeclaration> removed = new ArrayList(); for (int i = toremove.size()-2; i > -1; i--) { XSDElementDeclaration se = (XSDElementDeclaration) e.getSubstitutionGroup().remove(toremove.get(i).intValue()); removed.add(e); } //set the removed elements sub affiliation to a clone of the actual element for (XSDElementDeclaration se : removed) { if (se != null && e.equals(se.getSubstitutionGroupAffiliation())) { XSDElementDeclaration clone = (XSDElementDeclaration) e.cloneConcreteComponent(false, false); clone.setTargetNamespace(GML.NAMESPACE); se.setSubstitutionGroupAffiliation(clone); } } } e = e.getSubstitutionGroupAffiliation(); } } }