001package jmri.server.web.app; 002 003import com.fasterxml.jackson.databind.JsonNode; 004import com.fasterxml.jackson.databind.ObjectMapper; 005import java.io.File; 006import java.io.IOException; 007import java.net.URI; 008import java.net.URL; 009import java.util.ArrayList; 010import java.util.HashSet; 011import java.util.List; 012import java.util.Locale; 013import java.util.Set; 014import jmri.server.web.spi.AngularRoute; 015import jmri.server.web.spi.WebManifest; 016import jmri.server.web.spi.WebMenuItem; 017import jmri.util.FileUtil; 018import org.openide.util.lookup.ServiceProvider; 019import org.slf4j.Logger; 020import org.slf4j.LoggerFactory; 021 022/** 023 * Web Manifest built from manifest.json files. 024 * 025 * @author Randall Wood (C) 2016 026 */ 027@ServiceProvider(service = WebManifest.class) 028public class JsonManifest implements WebManifest { 029 030 private boolean initialized = false; 031 private final Set<WebMenuItem> menuItems = new HashSet<>(); 032 private final List<String> scripts = new ArrayList<>(); 033 private final List<String> styles = new ArrayList<>(); 034 private final List<String> dependencies = new ArrayList<>(); 035 private final Set<AngularRoute> routes = new HashSet<>(); 036 private final List<URL> sources = new ArrayList<>(); 037 private final Set<String> translations = new HashSet<>(); 038 private final static Logger log = LoggerFactory.getLogger(JsonManifest.class); 039 040 @Override 041 public Set<WebMenuItem> getNavigationMenuItems() { 042 this.initialize(); 043 return this.menuItems; 044 } 045 046 @Override 047 public List<String> getScripts() { 048 this.initialize(); 049 return this.scripts; 050 } 051 052 @Override 053 public List<String> getStyles() { 054 this.initialize(); 055 return this.styles; 056 } 057 058 @Override 059 public List<String> getAngularDependencies() { 060 this.initialize(); 061 return this.dependencies; 062 } 063 064 @Override 065 public Set<AngularRoute> getAngularRoutes() { 066 this.initialize(); 067 return this.routes; 068 } 069 070 @Override 071 public List<URL> getAngularSources() { 072 this.initialize(); 073 return this.sources; 074 } 075 076 @Override 077 public Set<URI> getPreloadedTranslations(Locale locale) { 078 this.initialize(); 079 Set<URI> found = new HashSet<>(); 080 this.translations.forEach((translation) -> { 081 URI url = FileUtil.findURI(translation.replaceFirst("\\*", locale.toString())); 082 if (url == null) { 083 url = FileUtil.findURI(translation.replaceFirst("\\*", locale.getLanguage())); 084 } 085 if (url == null) { 086 url = FileUtil.findURI(translation.replaceFirst("\\*", "en")); 087 } 088 if (url != null) { 089 found.add(url); 090 } else { 091 log.error("Unable to find localization file {} for any language", translation); 092 } 093 }); 094 return found; 095 } 096 097 synchronized private void initialize() { 098 if (!this.initialized) { 099 Set<File> manifests = FileUtil.findFiles("manifest.json", "web"); // NOI18N 100 ObjectMapper mapper = new ObjectMapper(); 101 manifests.forEach((manifest) -> { 102 try { 103 JsonNode root = mapper.readTree(manifest); 104 root.path("scripts").forEach((script) -> { 105 if (!this.scripts.contains(script.asText())) { 106 this.scripts.add(script.asText()); 107 } 108 }); 109 root.path("styles").forEach((style) -> { 110 if (!this.styles.contains(style.asText())) { 111 this.styles.add(style.asText()); 112 } 113 }); 114 root.path("dependencies").forEach((dependency) -> { 115 if (!this.dependencies.contains(dependency.asText())) { 116 this.dependencies.add(dependency.asText()); 117 } 118 }); 119 root.path("navigation").forEach((navigation) -> { 120 JsonMenuItem menuItem = new JsonMenuItem(navigation); 121 if (!this.menuItems.contains(menuItem)) { 122 this.menuItems.add(menuItem); 123 } 124 }); 125 root.path("routes").forEach((route) -> { 126 String when = route.path("when").asText(); // NOI18N 127 String template = route.path("template").asText(); // NOI18N 128 String controller = route.path("controller").asText(); // NOI18N 129 String redirection = route.path("redirection").asText(); // NOI18N 130 if (template.isEmpty() || controller.isEmpty()) { 131 template = null; 132 controller = null; 133 } 134 if (redirection.isEmpty()) { 135 redirection = null; 136 } 137 if (when != null && !when.isEmpty()) { 138 try { 139 this.routes.add(new AngularRoute(when, template, controller, redirection)); 140 } catch (NullPointerException | IllegalArgumentException ex) { 141 log.error("Unable to add route for {}", when); 142 } 143 } 144 }); 145 root.path("sources").forEach((source) -> { 146 URL url = FileUtil.findURL(source.asText()); 147 if (url != null) { 148 this.sources.add(url); 149 } 150 }); 151 root.path("translations").forEach((translation) -> { 152 String url = translation.asText(); 153 if (url != null) { 154 this.translations.add(url); 155 } 156 }); 157 } catch (IOException ex) { 158 log.error("Unable to read {}", manifest, ex); 159 } 160 }); 161 this.initialized = true; 162 } 163 } 164 165}