001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * http://www.geoapi.org 004 * 005 * Copyright (C) 2018-2019 Open Geospatial Consortium, Inc. 006 * All Rights Reserved. http://www.opengeospatial.org/ogc/legal 007 * 008 * Permission to use, copy, and modify this software and its documentation, with 009 * or without modification, for any purpose and without fee or royalty is hereby 010 * granted, provided that you include the following on ALL copies of the software 011 * and documentation or portions thereof, including modifications, that you make: 012 * 013 * 1. The full text of this NOTICE in a location viewable to users of the 014 * redistributed or derivative work. 015 * 2. Notice of any changes or modifications to the OGC files, including the 016 * date changes were made. 017 * 018 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE 019 * NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 020 * TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT 021 * THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY 022 * PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. 023 * 024 * COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 025 * CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. 026 * 027 * The name and trademarks of copyright holders may NOT be used in advertising or 028 * publicity pertaining to the software without specific, written prior permission. 029 * Title to copyright in this software and any associated documentation will at all 030 * times remain with copyright holders. 031 */ 032package org.opengis.geoapi; 033 034import java.util.Map; 035import java.util.Set; 036import java.util.HashMap; 037import java.util.HashSet; 038import java.util.Iterator; 039import javax.xml.namespace.QName; 040import org.opengis.annotation.UML; 041 042 043/** 044 * Mapping from XML prefixes or Java types to programmatic namespaces (modules or packages). 045 * There is not necessarily a one-to-one relationship between XML namespaces, Java packages 046 * or Python modules. For example we may merge some XML namespaces in a single programmatic 047 * namespace if keeping them separated would result in modules or packages with few classes. 048 * 049 * @author Martin Desruisseaux (Geomatys) 050 * @since 3.1 051 * @version 3.1 052 */ 053public final class NameSpaces { 054 /** 055 * Modifiable mapping from XML prefixes to packages (not necessarily Java packages). 056 * Keys are usually three-letters prefixes like {@code "cit"} for citations. 057 * Values are {@code "module/package"} strings, for example {@code "metadata/citation"}. 058 * Two keys may map to the same package if GeoAPI decides to merge some packages together. 059 */ 060 private final Map<String,String> prefixesToPackages; 061 062 /** 063 * Modifiable mapping from Java interfaces to packages (not necessarily Java packages). 064 * This is used for special cases before to test for {@link #prefixesToPackages}. 065 */ 066 private final Map<Class<?>,String> typesToPackages; 067 068 /** 069 * Creates a new mapping from XML namespaces (identified by prefixes) to programmatic namespaces. 070 */ 071 public NameSpaces() { 072 final Map<String,String> m = new HashMap<>(32); 073 m.put("gco", "metadata/naming"); 074 m.put("lan", "metadata/language"); 075 m.put("mcc", "metadata/maintenance"); // Default destination for commonClasses.xsd types not listed in 'typesToPackages'. 076 m.put("gex", "metadata/extent"); 077 m.put("cit", "metadata/citation"); 078 m.put("mmi", "metadata/maintenance"); 079 m.put("mrd", "metadata/distribution"); 080 m.put("mdt", "metadata/distribution"); // "transfer" merged with distribution. 081 m.put("mco", "metadata/constraints"); 082 m.put("mri", "metadata/identification"); 083 m.put("srv", "metadata/service"); 084 m.put("mac", "metadata/acquisition"); 085 m.put("mrc", "metadata/content"); 086 m.put("mrl", "metadata/lineage"); 087 m.put("mdq", "metadata/quality"); // "dataQuality" simplified to "quality" 088 m.put("mrs", "metadata/representation"); // "referenceSystem" merged with "spatialRepresentation" 089 m.put("msr", "metadata/representation"); // "spatialRepresentation" simplified to "representation" 090 m.put("mas", "metadata/extension"); // "applicationSchema" merged with "extension". 091 m.put("mex", "metadata/extension"); 092 m.put("mpc", "metadata/base"); // "portrayalCatalog" merged with "base". 093 m.put("mdb", "metadata/base"); 094 prefixesToPackages = m; 095 /* 096 * Types defined in "commonClasses.xsd". Types not listed below will go to "metadata/maintenance" 097 */ 098 final Map<Class<?>,String> t = new HashMap<>(8); 099 t.put(org.opengis.metadata.Identifier.class, "metadata/citation"); 100 t.put(org.opengis.metadata.identification.Progress.class, "metadata/identification"); 101 t.put(org.opengis.metadata.identification.BrowseGraphic.class, "metadata/identification"); 102 t.put(org.opengis.metadata.spatial.SpatialRepresentationType.class, "metadata/representation"); 103 /* 104 * Types having a different name in "dataQuality.xsd" because we have not yet updated that part. 105 */ 106 t.put(org.opengis.metadata.quality.TemporalAccuracy.class, "metadata/quality"); 107 typesToPackages = t; 108 } 109 110 /** 111 * Excludes the namespaces identified by the given prefixes. Calls to {@link #name(Class, Map)} 112 * for a type in the namespace identified by one of the given prefixes will return {@code null}. 113 * 114 * @param prefixes identifications of the namespaces to exclude. 115 */ 116 public void exclude(final String... prefixes) { 117 for (final String prefix : prefixes) { 118 final String ns = prefixesToPackages.put(prefix, null); 119 if (ns != null) { 120 for (final Iterator<String> it = typesToPackages.values().iterator(); it.hasNext();) { 121 if (ns.equals(it.next())) it.remove(); 122 } 123 } 124 } 125 } 126 127 /** 128 * Returns the OGC/ISO name of the given type together with its XML prefix and pseudo-namespace, or {@code null}. 129 * Note that while we use the {@link QName} object for convenience, this is <strong>not</strong> the XML name: 130 * 131 * <ul> 132 * <li>{@link QName#getLocalPart()} will be the OGC/ISO name, 133 * which is usually the same than the XML local part but not always.</li> 134 * <li>{@link QName#getPrefix()} will be the XML prefix if known, or the UML prefix otherwise. 135 * May be empty is no prefix can be inferred.</li> 136 * <li>{@link QName#getNamespaceURI()} will be the programmatic namespace. 137 * This may be a fragment of the XML namespace but never the full URI.</li> 138 * </ul> 139 * 140 * @param type the type for which to get the namespace, or {@code null}. 141 * @param definition value of {@link SchemaInformation#getTypeDefinition(Class)} for the given {@code type}, 142 * or {@code null} if unknown. 143 * @return the OGC/ISO name, prefix and pseudo-namespace for the given type, or {@code null} if none. 144 */ 145 public QName name(final Class<?> type, final Map<String, SchemaInformation.Element> definition) { 146 if (type != null) { 147 final UML uml = type.getAnnotation(UML.class); 148 if (uml != null) { 149 String prefix = null; 150 if (definition != null) { 151 SchemaInformation.Element def = definition.get(null); 152 if (def != null) { 153 prefix = def.prefix(); 154 } 155 } 156 String typeName = uml.identifier(); 157 final int splitAt = typeName.indexOf('_'); 158 if (prefix == null) { 159 if (splitAt > 0) { 160 prefix = typeName.substring(0, splitAt); 161 } else { 162 switch (type.getPackage().getName()) { 163 case "org.opengis.util": prefix = "gco"; break; 164 case "org.opengis.feature": prefix = "GF"; break; 165 default: { 166 switch (typeName) { 167 case "DirectPosition": prefix = "GM"; break; 168 default: prefix = ""; break; 169 } 170 } 171 } 172 } 173 } 174 typeName = typeName.substring(splitAt + 1); 175 if (!typeName.isEmpty()) { // Paranoiac check (should never happen). 176 String pkg = typesToPackages.get(type); 177 if (pkg == null) { 178 pkg = prefixesToPackages.get(prefix); 179 if (pkg == null) { 180 if (prefixesToPackages.containsKey(prefix)) { 181 return null; // Type explicitly excluded. 182 } 183 pkg = prefix; 184 } 185 } 186 return new QName(pkg, typeName, prefix); 187 } 188 } 189 } 190 return null; 191 } 192 193 /** 194 * Returns all namespace values that may be returned by {@link #name(Class, Map)}. 195 * This method returns a modifiable set. Modifications to the returned set will 196 * not affect this {@code NameSpaces} instance. 197 * 198 * @return all package names known to this {@code NameSpaces} instance. 199 */ 200 public Set<String> packages() { 201 final Set<String> pkg = new HashSet<>(prefixesToPackages.values()); 202 pkg.remove(null); 203 return pkg; 204 } 205}