/******************************************************************************* * Copyright 2012 Pearson Education * * 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 org.semantictools.frame.api; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import org.semantictools.context.renderer.MediaTypeFileManager; import org.semantictools.context.renderer.model.BibliographicReference; import org.semantictools.context.renderer.model.ContextProperties; import org.semantictools.context.renderer.model.DocumentMetadata; import org.semantictools.context.renderer.model.FrameConstraints; import org.semantictools.context.renderer.model.Person; import org.semantictools.context.renderer.model.ReferenceManager; import org.semantictools.context.renderer.model.SampleJson; public class ContextManager { // private static final Logger logger = LoggerFactory.getLogger(ContextManager.class); public static final String SKIP_VALIDATION = "skipValidation"; private static final String RDFTYPE = "rdfType"; private static final String RDFTYPE_REF = "rdfTypeRef"; private static final String RDF_PROPERTY = "rdfProperty"; private static final String MEDIATYPE = "mediaType"; private static final String MEDIATYPEURI = "mediaTypeURI"; private static final String MEDIATYPEREF = "mediaTypeRef"; private static final String CONTEXTURI = "contextURI"; private static final String CONTEXTREF = "contextRef"; private static final String GRAPH_TYPES = "graphTypes"; private static final String USE_PREFIX = "usePrefix"; private static final String SET = "@set"; private static final String ENABLE_VERSION_HISTORY = "enableVersionHistory"; private static final String IDREF = "idref"; private static final String REQUIRES_ID = "requiresId"; private static final String OPTIONAL = "optional"; private static final String MIXED_VALUE = "mixedValue"; private static final String STATUS = "status"; private static final String DATE = "date"; private static final String ABSTRACT = "abstract"; private static final String EDITORS = "editors"; private static final String AUTHORS = "authors"; private static final String TITLE = "title"; private static final String SAMPLE_TEXT = "sampleText"; private static final String EXCLUDE_TYPE = "excludeType"; private static final String INTRODUCTION = "introduction"; private static final String SIMPLE_NAMES = "simpleNames"; private static final String INCLUDES_SUFFIX = ".includes"; private static final String EXCLUDES_SUFFIX = ".excludes"; private static final String EXCLUDE_SUBTYPES_SUFFIX = ".excludeSubtypes"; private static final String PURL_DOMAIN = "purlDomain"; private static final String EXPANDED_VALUE = "expandedValue"; private static final String CAPTION_SUFFIX = ".caption"; private static final String TEMPLATE = "template"; private static final String VALIDATE_JSON_SAMPLES = "validateJsonSamples"; private static final String HOW_TO_READ = "includeHowToReadSection"; private static final String JSON_LD_SECTION = "includeJsonldSection"; private static final String RESERVED_WORDS = "includeReservedWordsSection"; private static final String MEDIA_TYPE_SECTION = "includeMediaTypeConformance"; private static final String OVERVIEW_DIAGRAM = "includeOverviewDiagram"; private MediaTypeFileManager fileManager; private Map<String, ContextProperties> contextMap = new HashMap<String, ContextProperties>(); private DocumentMetadata defaultMetadata; public ContextManager(DocumentMetadata defaultMetadata, MediaTypeFileManager fileManager) { this.fileManager = fileManager; this.defaultMetadata = defaultMetadata; } public MediaTypeFileManager getMediaTypeFileManager() { return fileManager; } /** * Loads ContextProperties from a properties file. * The file must contain properties of the following form: * <UL> * <LI> *.rdfType = <The fully qualified URI for the RDF class to which the representation applies> * <LI> *.mediaType = <The MIME type for the JSON-LD context> * <LI> *.contextURI = <The fully qualified URI at which the JSON-LD context will be published< * <LI> *.idref = <A space separated list of URIs for properties that will be coerced to "@id" values in the JSON-LD context> * </UL> * @param propertiesFile * @throws IOException */ public void loadContextProperties(File propertiesFile) throws IOException, ContextPropertiesSyntaxException { Properties properties = new Properties(); FileInputStream input = new FileInputStream(propertiesFile); try { properties.load(input); parseProperties(propertiesFile, properties); } finally { input.close(); } } public void loadAll(File baseDir) throws ContextPropertiesSyntaxException, IOException { File[] list = baseDir.listFiles(); if (list == null) return; for (int i=0; i<list.length; i++) { File file = list[i]; if (file.isDirectory()) { loadAll(file); } else if ("context.properties".equals(file.getName())) { loadContextProperties(file); } } } /** * Generates the default name for a media type that contains the identifier * for a resource. This is used as the media type for the response from a * POST method. * @param baseMediaType The base media type for the resource */ public String createIdMediaTypeName(String baseMediaType) { int plus = baseMediaType.lastIndexOf('+'); if (plus < 0) return baseMediaType + ".id"; return baseMediaType.substring(0, plus) + ".id" + baseMediaType.substring(plus); } public String createIdMediaTypeRef(String typeName) { return "[" + typeName + "-id-media-type]"; } public ContextProperties getContextPropertiesByMediaType(String mediaType) { return contextMap.get(mediaType); } public List<ContextProperties> listContextPropertiesForClass(String rdfClassURI) { List<ContextProperties> list = new ArrayList<ContextProperties>(); for (ContextProperties p : contextMap.values()) { if (rdfClassURI.equals(p.getRdfTypeURI())) { list.add(p); } } return list; } public List<ContextProperties> listContextProperties() { return new ArrayList<ContextProperties>( contextMap.values() ); } private void parseProperties(File sourceFile, Properties properties) { ContextProperties sink = new ContextProperties(defaultMetadata, properties); sink.setSourceFile(sourceFile); for (Map.Entry<Object, Object> e : properties.entrySet()) { String key = e.getKey().toString(); String value = e.getValue().toString(); if (key.startsWith("[")) { sink.putReference(key, value); continue; } if (CONTEXTURI.equals(key)) { sink.setContextURI(value); } else if (IDREF.equals(key)) { setIdref(sink, value); } else if (REQUIRES_ID.equals(key)) { setRequiresId(sink, value); } else if (MIXED_VALUE.equals(key)) { setMixedValue(sink, value); } else if (MEDIATYPE.equals(key)) { sink.setMediaType(value); } else if (OPTIONAL.equals(key)) { addOptionalProperties(sink, value); } else if (MEDIATYPEURI.equals(key)) { sink.setMediaTypeURI(value); } else if (MEDIATYPEREF.equals(key)) { sink.setMediaTypeRef(value); } else if (USE_PREFIX.equals(key)) { setUsePrefix(sink, value); } else if (RDFTYPE.equals(key)) { sink.setRdfTypeURI(value); } else if (RDFTYPE_REF.equals(key)) { sink.setRdfTypeRef(value); } else if (RDF_PROPERTY.equals(key)) { sink.setRdfProperty(value); } else if (GRAPH_TYPES.equals(key)) { setGraphTypes(sink, value); } else if (SET.equals(key)) { setSetProperties(sink, value); } else if (CONTEXTREF.equals(key)) { sink.setContextRef(value); } else if (STATUS.equals(key)) { sink.setStatus(value); } else if (DATE.equals(key)) { sink.setDate(value); } else if (ABSTRACT.equals(key)) { sink.setAbstactText(value); } else if (ENABLE_VERSION_HISTORY.equals(key)) { sink.setHistoryLink("true".equalsIgnoreCase(value)); } else if (EDITORS.equals(key)) { setEditors(sink, value); } else if (AUTHORS.equals(key)) { setAuthors(sink, value); } else if (INTRODUCTION.equals(key)) { sink.setIntroduction(value); } else if (TITLE.equals(key)) { sink.setTitle(value); } else if (SIMPLE_NAMES.equals(key)) { setSimpleNames(sink, value); } else if (SAMPLE_TEXT.equals(key)) { sink.setSampleText(value); } else if (EXCLUDE_TYPE.equals(key)) { setExcludedTypes(sink, value); } else if (key.endsWith(INCLUDES_SUFFIX)) { addIncludesConstraint(sink, key, value); } else if (key.endsWith(EXCLUDES_SUFFIX)) { addExcludesConstraint(sink, key, value); } else if (key.endsWith(EXCLUDE_SUBTYPES_SUFFIX)) { addExcludeSubtypesConstraint(sink, key, value); } else if (key.endsWith(CAPTION_SUFFIX)) { addCaption(sink, key, value); } else if (PURL_DOMAIN.equals(key)) { sink.setPurlDomain(value); } else if (EXPANDED_VALUE.equals(key)) { setExpandedValue(sink, value); } else if (TEMPLATE.equals(key)) { sink.setTemplateName(value); } else if (VALIDATE_JSON_SAMPLES.equals(key)) { sink.setValidateJsonSamples("true".equalsIgnoreCase(value)); } else if (HOW_TO_READ.equals(key)) { sink.setHowToReadThisDocument(toBoolean(value)); } else if (RESERVED_WORDS.equals(key)) { sink.setReservedTermsSection(toBoolean(value)); } else if (JSON_LD_SECTION.equals(key)) { sink.setReservedTermsSection(toBoolean(value)); } else if (MEDIA_TYPE_SECTION.equals(key)) { sink.setMediaTypeSection(toBoolean(value)); } else if (OVERVIEW_DIAGRAM.equals(key)) { sink.setOverviewDiagram(toBoolean(value)); } } validate(sink); setDefaults(sink); } private void setSimpleNames(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.addSimpleName(tokenizer.nextToken()); } } boolean toBoolean(String text) { return "true".equalsIgnoreCase(text); } private void addOptionalProperties(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.addOptional(tokenizer.nextToken()); } } private void setUsePrefix(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.addUsePrefix(tokenizer.nextToken()); } } private void setSetProperties(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.addSetProperty(tokenizer.nextToken()); } } private void setGraphTypes(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.addGraphType(tokenizer.nextToken()); } } private void setRequiresId(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.getRequiresId().add(tokenizer.nextToken()); } } private void setExcludedTypes(ContextProperties sink, String value) { StringTokenizer tokenizer = new StringTokenizer(value, " \t\r\n"); while (tokenizer.hasMoreElements()) { sink.getExcludedTypes().add(tokenizer.nextToken()); } } private void addCaption(ContextProperties sink, String key, String value) { int dot = key.lastIndexOf('.'); String fileName = key.substring(0, dot); for (SampleJson sample : sink.getSampleJsonList()) { if (fileName.equals(sample.getFileName())) { sample.setFileName(value); return; } } SampleJson sample = new SampleJson(); sample.setFileName(fileName); sample.setCaption(value); sink.getSampleJsonList().add(sample); } private void setExpandedValue(ContextProperties sink, String value) { StringTokenizer tokens = new StringTokenizer(value, " \t\r\n"); while (tokens.hasMoreTokens()) { String propertyURI = tokens.nextToken(); sink.getExpandedValues().add(propertyURI); } } private void addIncludesConstraint(ContextProperties sink, String key, String value) { int dot = key.lastIndexOf('.'); String name = key.substring(0, dot); FrameConstraints constraints = sink.fetchFrameConstraints(name); StringTokenizer tokens = new StringTokenizer(value, " \r\n\t"); while (tokens.hasMoreTokens()) { String propertyURI = tokens.nextToken(); constraints.addIncludedProperty(propertyURI); } } private void addExcludesConstraint(ContextProperties sink, String key, String value) { int dot = key.lastIndexOf('.'); String name = key.substring(0, dot); FrameConstraints constraints = sink.fetchFrameConstraints(name); StringTokenizer tokens = new StringTokenizer(value, " \r\n\t"); while (tokens.hasMoreTokens()) { String propertyURI = tokens.nextToken(); constraints.addExcludedProperty(propertyURI); } } private void addExcludeSubtypesConstraint(ContextProperties sink, String key, String value) { int dot = key.lastIndexOf('.'); String name = key.substring(0, dot); FrameConstraints constraints = sink.fetchFrameConstraints(name); StringTokenizer tokens = new StringTokenizer(value, " \r\n\t"); while (tokens.hasMoreTokens()) { String propertyURI = tokens.nextToken(); constraints.addExcludesSubtype(propertyURI); } } private void setDefaults(ContextProperties sink) { setTitle(sink); setMediaTypeDocFile(sink); setMediaTypeReference(sink); // setJsonLdContextReference(sink); } private void setMediaTypeReference(ContextProperties properties) { ReferenceManager manager = properties.getReferenceManager(); if (manager == null) return; String mediaType = properties.getMediaType(); String mediaTypeRef = properties.getMediaTypeRef(); if (mediaTypeRef == null) { String[] array = mediaType.split("\\./\\+"); String rdfType = properties.getRdfTypeURI(); if (rdfType != null) { String localName = TypeManager.getLocalName(rdfType); String suffix = getSuffix(localName, array); mediaTypeRef = "[" + localName + suffix + "-Media-Type" + "]"; properties.setMediaTypeRef(mediaTypeRef); } } if (mediaTypeRef == null) { mediaTypeRef = "[" + mediaType + "]"; } File localFile = fileManager.getMediaTypeDocumentationFile(mediaType); properties.setLocalFile(localFile); BibliographicReference ref = new BibliographicReference(); ref.setLabel(mediaTypeRef); ref.setAuthor(properties.getEditors()); ref.setTitle(properties.getTitle()); ref.setDate(properties.getDate()); ref.setEdition(properties.getStatus()); ref.setLocalFile(localFile); manager.add(ref); } private String getSuffix(String localName, String[] array) { for (int i=0; i<array.length-1; i++) { if (localName.equals(array[i])) { return "-" + array[i+1]; } } return ""; } // // private void setJsonLdContextReference(ContextProperties sink) { // String contextRef = sink.getContextRef(); // if (contextRef == null) return; // contextRef = contextRef.replace(" ", " "); // // if (sink.getReference(contextRef) != null) return; // // StringBuilder builder = new StringBuilder(); // List<Person> authors = sink.getAuthors(); // String comma = ""; // for (Person author : authors) { // builder.append(comma); // builder.append(author.getPersonName()); // comma = ", "; // } // builder.append("| "); // String rdfTypeURI = sink.getRdfTypeURI(); // String typeName = TypeManager.getLocalName(rdfTypeURI); // String title = "JSON-LD Context for " + typeName + " Resources"; // builder.append(title); // builder.append("| "); // String status = sink.getStatus(); // if (status != null) { // builder.append(status); // } // String date = sink.getDate(); // if (date != null) { // if (status != null) { // builder.append(", "); // } // builder.append(date); // } // builder.append("| "); // builder.append("URL: "); // String contextURL = sink.getContextURI(); // builder.append(contextURL); // // sink.putReference(contextRef, builder.toString()); // // } private void setMediaTypeDocFile(ContextProperties sink) { File file = fileManager.getIndexFile(sink.getMediaType()); sink.setMediaTypeDocFile(file); } private String getLocalName(String uri) { return TypeManager.getLocalName(uri); } private void setTitle(ContextProperties sink) { if (sink.getTitle() == null) { String typeURI = sink.getRdfTypeURI(); String mediaType = sink.getMediaType(); String title = null; if (typeURI == null) { title = "The <code>" + mediaType + "</code> format"; } else { String typeName = getLocalName(typeURI); title = typeName + " JSON Binding<br>in the <code>" + mediaType + "</code> format"; } sink.setTitle(title); } } private void setEditors(ContextProperties sink, String value) { StringTokenizer tokens = new StringTokenizer(value, "\n"); while (tokens.hasMoreTokens()) { String text = tokens.nextToken().trim(); if (text.length()>0) { sink.addEditor(parsePerson(text)); } } } private Person parsePerson(String line) { String personName = line; String orgName = null; int comma = line.indexOf(','); if (comma > 0) { personName = line.substring(0, comma).trim(); orgName = line.substring(comma+1).trim(); } Person person = new Person(); person.setPersonName(personName); person.setOrgName(orgName); return person; } private void setAuthors(ContextProperties sink, String value) { StringTokenizer tokens = new StringTokenizer(value, "\n"); while (tokens.hasMoreTokens()) { String text = tokens.nextToken().trim(); if (text.length()>0) { sink.addAuthor(parsePerson(text)); } } } private void validate(ContextProperties p) { StringBuilder error = new StringBuilder(); validate(error, p); if (error.length()>0) { throw new ContextPropertiesSyntaxException(error.toString()); } contextMap.put(p.getMediaType(), p); } private void validate(StringBuilder error, ContextProperties p) { // if (p.getContextURI() == null) { // append(error, CONTEXTURI); // } if (p.getMediaType() == null) { append(error, MEDIATYPE); } // if (p.getRdfTypeURI() == null) { // append(error, RDFTYPE); // } } private void append(StringBuilder error, String propertyName) { if (error.length() > 0) { error.append("\n"); } else { error.append("Missing properties:\n"); } error.append(" "); error.append(propertyName); } private void setMixedValue(ContextProperties sink, String value) { StringTokenizer tokens = new StringTokenizer(value, " \t\r\n"); while (tokens.hasMoreTokens()) { String propertyURI = tokens.nextToken(); sink.addMixed(propertyURI); } } private void setIdref(ContextProperties sink, String value) { StringTokenizer tokens = new StringTokenizer(value, " \t\r\n"); while (tokens.hasMoreTokens()) { String idref = tokens.nextToken(); sink.addIdRef(idref); } } }