001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * Copyright 2015 ForgeRock AS. 024 */ 025package org.forgerock.opendj.maven.doc; 026 027import static com.forgerock.opendj.ldap.CoreMessages.*; 028import static org.forgerock.opendj.maven.doc.Utils.*; 029 030import org.apache.maven.plugin.AbstractMojo; 031import org.apache.maven.plugin.MojoExecutionException; 032import org.apache.maven.plugin.MojoFailureException; 033import org.apache.maven.plugins.annotations.Mojo; 034import org.apache.maven.plugins.annotations.Parameter; 035import org.forgerock.opendj.ldap.schema.CoreSchemaSupportedLocales; 036 037import java.io.File; 038import java.io.IOException; 039import java.text.SimpleDateFormat; 040import java.util.Date; 041import java.util.HashMap; 042import java.util.LinkedList; 043import java.util.List; 044import java.util.Locale; 045import java.util.Map; 046import java.util.Set; 047import java.util.TreeMap; 048 049/** 050 * Generate schema-related reference documentation sources. 051 */ 052@Mojo(name = "generate-schema-ref") 053public class GenerateSchemaDocMojo extends AbstractMojo { 054 /** The locale for which to generate the documentation. */ 055 @Parameter(defaultValue = "en") 056 private String locale; 057 058 /** Output directory for source files. */ 059 @Parameter(defaultValue = "${project.build.directory}/docbkx-sources/shared") 060 private File outputDirectory; 061 062 /** 063 * Writes schema reference documentation source files. 064 * @throws MojoExecutionException Not used. 065 * @throws MojoFailureException Failed to write a file. 066 */ 067 @Override 068 public void execute() throws MojoExecutionException, MojoFailureException { 069 final Locale currentLocale = getLocaleFromTag(locale); 070 final String localeReference = getLocalesAndSubTypesDocumentation(currentLocale); 071 final File localeReferenceFile = new File(outputDirectory, "sec-locales-subtypes.xml"); 072 try { 073 writeStringToFile(localeReference, localeReferenceFile); 074 } catch (IOException e) { 075 throw new MojoExecutionException("Failed to write " + localeReferenceFile.getPath()); 076 } 077 } 078 079 /** 080 * Returns a DocBook XML Section element documenting supported locales and language subtypes. 081 * @param currentLocale The locale for which to generate the documentation. 082 * @return A DocBook XML Section element documenting supported locales and language subtypes. 083 */ 084 private String getLocalesAndSubTypesDocumentation(final Locale currentLocale) { 085 final Map<String, Object> map = new HashMap<>(); 086 map.put("year", new SimpleDateFormat("yyyy").format(new Date())); 087 map.put("lang", getTagFromLocale(currentLocale)); 088 map.put("title", DOC_LOCALE_SECTION_TITLE.get()); 089 map.put("info", DOC_LOCALE_SECTION_INFO.get()); 090 map.put("locales", getLocalesDocMap(currentLocale)); 091 map.put("subtypes", getSubTypesDocMap(currentLocale)); 092 return applyTemplate("sec-locales-subtypes.ftl", map); 093 } 094 095 private final Map<String, String> localeTagsToOids = 096 CoreSchemaSupportedLocales.getJvmSupportedLocaleNamesToOids(); 097 098 /** Container for documentation regarding a locale. */ 099 private class LocaleDoc { 100 String tag; 101 String language; 102 String oid; 103 } 104 105 /** 106 * Returns a map of languages to Locale documentation containers. 107 * @param currentLocale The Locale of the resulting documentation. 108 * @return A map of languages to Locale documentation containers. 109 */ 110 private Map<String, LocaleDoc> getLanguagesToLocalesMap(final Locale currentLocale) { 111 Map<String, LocaleDoc> locales = new TreeMap<>(); 112 for (String tag : localeTagsToOids.keySet()) { 113 final Locale locale = getLocaleFromTag(tag); 114 if (locale == null) { 115 continue; 116 } 117 final LocaleDoc localeDoc = new LocaleDoc(); 118 localeDoc.tag = tag; 119 localeDoc.language = locale.getDisplayName(currentLocale); 120 localeDoc.oid = localeTagsToOids.get(tag); 121 if (!localeDoc.language.equals(localeDoc.tag)) { 122 // No display language so must not be supported in current JVM 123 locales.put(localeDoc.language, localeDoc); 124 } else if (localeDoc.tag.equals("sh")) { 125 localeDoc.language = DOC_LANGUAGE_SH.get().toString(currentLocale); 126 locales.put(localeDoc.language, localeDoc); 127 } 128 } 129 return locales; 130 } 131 132 /** 133 * Returns a map of information for documenting supported locales. 134 * @param currentLocale The locale for which to generate the information. 135 * @return A map of information for documenting supported locales. 136 */ 137 private Map<String, Object> getLocalesDocMap(final Locale currentLocale) { 138 final Map<String, Object> result = new HashMap<>(); 139 result.put("title", DOC_SUPPORTED_LOCALES_TITLE.get()); 140 result.put("indexTerm", DOC_SUPPORTED_LOCALES_INDEXTERM.get()); 141 final Map<String, LocaleDoc> localesMap = getLanguagesToLocalesMap(currentLocale); 142 final Set<String> sortedLanguages = localesMap.keySet(); 143 final List<Map<String, Object>> locales = new LinkedList<>(); 144 for (final String language : sortedLanguages) { 145 final LocaleDoc locale = localesMap.get(language); 146 final Map<String, Object> map = new HashMap<>(); 147 map.put("language", locale.language); 148 map.put("tag", DOC_LOCALE_TAG.get(locale.tag)); 149 map.put("oid", DOC_LOCALE_OID.get(locale.oid)); 150 locales.add(map); 151 } 152 result.put("locales", locales); 153 return result; 154 } 155 156 /** 157 * Returns a map of information for documenting supported language subtypes. 158 * @param currentLocale The locale for which to generate the information. 159 * @return A map of information for documenting supported language subtypes. 160 */ 161 private Map<String, Object> getSubTypesDocMap(final Locale currentLocale) { 162 final Map<String, Object> result = new HashMap<>(); 163 result.put("title", DOC_SUPPORTED_SUBTYPES_TITLE.get()); 164 result.put("indexTerm", DOC_SUPPORTED_SUBTYPES_INDEXTERM.get()); 165 final List<Map<String, Object>> locales = new LinkedList<>(); 166 for (final String tag : localeTagsToOids.keySet()) { 167 final Map<String, Object> map = new HashMap<>(); 168 int idx = tag.indexOf('-'); 169 if (idx == -1) { 170 final Locale locale = getLocaleFromTag(tag); 171 if (locale == null) { 172 continue; 173 } 174 final String language = locale.getDisplayName(currentLocale); 175 if (!language.equals(tag)) { 176 map.put("language", language); 177 map.put("tag", tag); 178 } else if (tag.equals("sh")) { 179 map.put("language", DOC_LANGUAGE_SH.get().toString(currentLocale)); 180 map.put("tag", tag); 181 } 182 if (!map.isEmpty()) { 183 locales.add(map); 184 } 185 } 186 } 187 result.put("locales", locales); 188 return result; 189 } 190 191 /** 192 * Returns the Locale based on the tag, or null if the tag is null. 193 * <br> 194 * Java 6 is missing {@code Locale.forLanguageTag()}. 195 * @param tag The tag for the locale, such as {@code en_US}. 196 * @return The Locale based on the tag, or null if the tag is null. 197 */ 198 private Locale getLocaleFromTag(final String tag) { 199 if (tag == null) { 200 return null; 201 } 202 203 // Apparently Locale tags can include not only languages and countries, 204 // but also variants, e.g. es_ES_Traditional_WIN. 205 // Pull these apart to be able to construct the locale. 206 // 207 // OpenDJ does not seem to have any locales with variants, but just in case... 208 // The separator in OpenDJ seems to be '-'. 209 // @see CoreSchemaSupportedLocales#LOCALE_NAMES_TO_OIDS 210 final char sep = '-'; 211 int langIdx = tag.indexOf(sep); 212 final String lang; 213 if (langIdx == -1) { 214 // No country or variant 215 return new Locale(tag); 216 } else { 217 lang = tag.substring(0, langIdx); 218 } 219 220 int countryIdx = tag.indexOf(sep, langIdx + 1); 221 final String country; 222 if (countryIdx == -1) { 223 // No variant 224 country = tag.substring(langIdx + 1); 225 return new Locale(lang, country); 226 } else { 227 country = tag.substring(langIdx + 1, countryIdx); 228 final String variant = tag.substring(countryIdx + 1); 229 return new Locale(lang, country, variant); 230 } 231 } 232 233 /** 234 * Returns the tag based on the Locale, or null if the Locale is null. 235 * <br> 236 * Java 6 is missing {@code Locale.toLanguageTag()}. 237 * @param locale The Locale for which to return the tag. 238 * @return The tag based on the Locale, or null if the Locale is null. 239 */ 240 private String getTagFromLocale(final Locale locale) { 241 if (locale == null) { 242 return null; 243 } 244 245 final String lang = locale.getLanguage(); 246 final String country = locale.getCountry(); 247 final String variant = locale.getVariant(); 248 final char sep = '-'; 249 StringBuilder tag = new StringBuilder(); 250 if (lang != null) { 251 tag.append(lang); 252 } 253 if (country != null && !country.isEmpty()) { 254 tag.append(sep).append(country); 255 } 256 if (variant != null && !variant.isEmpty()) { 257 tag.append(sep).append(variant); 258 } 259 return tag.toString(); 260 } 261}