/** * Copyright (c) 2015-2016 Inria * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * - Philippe Merle <philippe.merle@inria.fr> */ package org.occiware.mart.server; import java.util.ArrayList; import java.util.List; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.io.BufferedReader; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.occiware.clouddesigner.occi.Action; import org.occiware.clouddesigner.occi.Attribute; import org.occiware.clouddesigner.occi.AttributeState; import org.occiware.clouddesigner.occi.Category; import org.occiware.clouddesigner.occi.Configuration; import org.occiware.clouddesigner.occi.Entity; import org.occiware.clouddesigner.occi.Extension; import org.occiware.clouddesigner.occi.Link; import org.occiware.clouddesigner.occi.Kind; import org.occiware.clouddesigner.occi.Mixin; import org.occiware.clouddesigner.occi.OCCIFactory; import org.occiware.clouddesigner.occi.OCCIRegistry; import org.occiware.clouddesigner.occi.OcciCoreConstants; import org.occiware.clouddesigner.occi.Resource; import org.occiware.clouddesigner.occi.util.OcciHelper; public class OcciServlet extends HttpServlet { /** * Name of the init parameter in web.xml files. * * <init-param> * <param-name>occi-configuration-file</param-name> * <param-value>model/infrastructure.occic</param-value> * </init-param> */ // public static String INIT_PARAM_NAME_OCCI_CONFIGURATION_FILE = "occi-configuration-file"; // public static String DEFAULT_OCCI_CONFIGURATION_FILE = "model/infrastructure.occic"; /** * The OCCI model managed by this servlet. */ // private Model model; private Configuration configuration; // TO REMOVE public Map<String, Entity> entities; // TO REMOVE public Map<String, List<Entity>> collections; // TO REMOVE // TODO: use the Web server URL public static final String OCCI_SERVER = "http://localhost:9090"; public static final String TEXT_PLAIN = "text/plain"; public static final String TEXT_OCCI = "text/occi"; public static String CATEGORY = "Category"; public static String X_OCCI_LOCATION = "X-OCCI-Location"; public static String X_OCCI_ATTRIBUTE = "X-OCCI-Attribute"; public static String OCCI_CORE_ID = "occi.core.id"; public static String OCCI_CORE_SOURCE = "occi.core.source"; public static String OCCI_CORE_TARGET = "occi.core.target"; void addToCollection(Entity entity, Category category) { String collectionLocation = getLocation(category); List<Entity> collection = collections.get(collectionLocation); if(collection == null) { collection = new ArrayList<Entity>(); collections.put(collectionLocation, collection); } collection.add(entity); } void addToCollections(Entity entity) { addToCollection(entity, entity.getKind()); for(Mixin mixin : entity.getMixins()) { addToCollection(entity, mixin); } } /** * */ private static final long serialVersionUID = -1341053999425047651L; @Override public void init() { // Load the OCCI configuration to use. // String occiConfigurationFile = getInitParameter(INIT_PARAM_NAME_OCCI_CONFIGURATION_FILE); // if(occiConfigurationFile == null) { // occiConfigurationFile = DEFAULT_OCCI_CONFIGURATION_FILE; // } // log("OcciServlet.init - Loading " + occiConfigurationFile + "..."); // Configuration configuration = Main.loadConfiguration(occiConfigurationFile); // log("OcciServlet.init - " + occiConfigurationFile + " loaded."); // model = new ModelImpl(configuration); configuration = OCCIFactory.eINSTANCE.createConfiguration(); for(String scheme : OCCIRegistry.getInstance().getRegisteredExtensions()) { configuration.getUse().add(OcciHelper.loadExtension(scheme)); } // TO REMOVE entities = new HashMap<String, Entity>(); collections = new HashMap<String, List<Entity>>(); for(Resource resource : configuration.getResources()) { entities.put(getLocation(resource), resource); addToCollections(resource); for(Link link : resource.getLinks()) { entities.put(getLocation(link), link); addToCollections(link); } } org.occiware.mart.MART.reportJavaInformation(); } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestPathInfo = request.getPathInfo(); log("doPost - " + requestPathInfo); BufferedReader reader = request.getReader(); String line; // Read the first line containing a category. line = reader.readLine(); log("doPost - line=" + line); if(line == null) { log("doPost - No category provided!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } Matcher matcher = PATTERN_CATEGORY.matcher(line); if (!matcher.find()) { log("doPost - Not a category!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } String term = matcher.group(GROUP_TERM); String scheme = matcher.group(GROUP_SCHEME); String categoryClass = matcher.group(GROUP_CLASS); Kind kind = null; switch(categoryClass) { case CLASS_KIND: for(Extension extension : configuration.getUse()) { for(Kind k : extension.getKinds()) { if(term.equals(k.getTerm()) && scheme.equals(k.getScheme())) { kind = k; break; } } } if(kind == null) { log("doPost - kind " + scheme + term + " not found!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } break; default: log("doPost - must be kind!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } Kind tmp = kind; while(tmp != null) { if(OcciCoreConstants.OCCI_CORE_SCHEME.equals(tmp.getScheme())) { break; } tmp = tmp.getParent(); } if(tmp == null) { log("doPost - don't know if this kind is a resource or a link"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } Entity entity = null; switch(tmp.getTerm()) { case "resource": entity = OCCIFactory.eINSTANCE.createResource(); break; case "link": entity = OCCIFactory.eINSTANCE.createLink(); break; default: log("doPost - don't know if this kind is a resource or a link"); response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } entity.setKind(kind); line = reader.readLine(); log("doPost - line=" + line); if(line != null) { while(true) { matcher = PATTERN_CATEGORY.matcher(line); if (!matcher.find()) { break; } term = matcher.group(GROUP_TERM); scheme = matcher.group(GROUP_SCHEME); categoryClass = matcher.group(GROUP_CLASS); Mixin mixin = null; switch(categoryClass) { case CLASS_MIXIN: for(Extension extension : configuration.getUse()) { for(Mixin m : extension.getMixins()) { if(term.equals(m.getTerm()) && scheme.equals(m.getScheme())) { mixin = m; break; } } } if(mixin == null) { log("doPost - mixin " + scheme + term + " not found!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } break; default: log("doPost - must be mixin!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } entity.getMixins().add(mixin); line = reader.readLine(); log("doPost - line=" + line); } } while(line != null) { if(!line.startsWith(X_OCCI_ATTRIBUTE)) { log("doPost - must be X-OCCI-Attributes!"); response.setStatus(HttpServletResponse.SC_BAD_REQUEST); return; } line = line.replaceAll(X_OCCI_ATTRIBUTE, ""); line = line.replaceAll(": ", ""); String[] parts = line.split("="); // TODO: deal with occi.core.source and occi.core.target if(!OCCI_CORE_ID.equals(parts[0])) { AttributeState attribute = OCCIFactory.eINSTANCE.createAttributeState(); attribute.setName(parts[0]); attribute.setValue(parts[1]); entity.getAttributes().add(attribute); } line = reader.readLine(); log("doPost - line=" + line); } if(entity instanceof Resource) { configuration.getResources().add((Resource)entity); } entities.put(getLocation(entity), entity); addToCollections(entity); log("doPost - " + getNetworkLocation(entity) + " created."); response.setStatus(HttpServletResponse.SC_CREATED); response.setContentType(TEXT_PLAIN); PrintWriter out = response.getWriter(); out.println(X_OCCI_LOCATION + ": " + getNetworkLocation(entity)); } @Override public void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestPathInfo = request.getPathInfo(); log("doDelete - " + requestPathInfo); Entity entity = entities.get(requestPathInfo); if(entity == null) { log("doDelete - " + requestPathInfo + " not found"); response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } entities.remove(requestPathInfo); collections.get(getLocation(entity.getKind())).remove(entity); if(entity instanceof Resource) { configuration.getResources().remove(entity); } else { Link link = (Link)entity; link.setSource(null); } log("doDelete - " + requestPathInfo + " deleted."); response.setStatus(HttpServletResponse.SC_OK); } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { // TODO: following code can be refactored: a class for getAllEntities, getAllCategories, getEntity, getCollection String requestPathInfo = request.getPathInfo(); if("/".equals(requestPathInfo)) { getAllEntities(request, response); } else if("/-/".equals(requestPathInfo)) { getAllCategories(request, response); } else { Entity entity = entities.get(requestPathInfo); if(entity != null) { getEntity(entity, request, response); } else { List<Entity> collection = collections.get(requestPathInfo); if(collection != null) { getCollection(collection, request, response); } else { PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<body>"); out.println("<h1>Hello Servlet Get</h1>"); out.println("<li> pathInfo = " + request.getPathInfo()); out.println("<li> contentType = " + request.getContentType()); out.println("<li> servletPath = " + request.getServletPath()); java.util.Enumeration<String> headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()) { String name = headerNames.nextElement(); out.println("<li> " + name + " = " + request.getHeader(name)); } out.println("</body>"); out.println("</html>"); } } } } @Override public void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException { String requestPathInfo = request.getPathInfo(); log("doPut - " + requestPathInfo); BufferedReader reader = request.getReader(); String line = reader.readLine(); while(line != null) { log("doPut - line=" + line); line = reader.readLine(); } response.setStatus(HttpServletResponse.SC_NOT_IMPLEMENTED); } public void getEntity(Entity entity, HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); response.setContentType(TEXT_PLAIN); out.println(CATEGORY + ": " + asCategory(entity.getKind(), false)); for(Mixin mixin : entity.getMixins()) { out.println(CATEGORY + ": " + asCategory(mixin, false)); } out.println(X_OCCI_ATTRIBUTE + ": " + OCCI_CORE_ID + "=\"" + entity.getId() + '\"'); if(entity instanceof Link) { Link link = (Link)entity; out.println(X_OCCI_ATTRIBUTE + ": " + OCCI_CORE_SOURCE + "=\"" + getLocation(link.getSource()) + '\"'); out.println(X_OCCI_ATTRIBUTE + ": " + OCCI_CORE_TARGET + "=\"" + getLocation(link.getTarget()) + '\"'); } for(AttributeState attribute : entity.getAttributes()) { String value = attribute.getValue(); // TODO: add " " only for string and enum attributes. value = "\"" + value + "\""; out.println(X_OCCI_ATTRIBUTE + ": " + attribute.getName() + '=' + value); } } public void getAllEntities(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); response.setContentType(TEXT_PLAIN); for(Resource resource : configuration.getResources()) { out.println(X_OCCI_LOCATION + ": " + getNetworkLocation(resource)); for(Link link : resource.getLinks()) { out.println(X_OCCI_LOCATION + ": " + getNetworkLocation(link)); } } } public void getCollection(List<Entity> collection, HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); response.setContentType(TEXT_PLAIN); for(Entity entity : collection) { out.println(X_OCCI_LOCATION + ": " + getNetworkLocation(entity)); } } String getLocation(Category category) { return '/' + category.getTerm() + '/'; } String getLocation(Entity entity) { return getLocation(entity.getKind()) + entity.getId(); } String getNetworkLocation(Entity entity) { return OCCI_SERVER + getLocation(entity); } public void getAllCategories(HttpServletRequest request, HttpServletResponse response) throws IOException { PrintWriter out = response.getWriter(); response.setContentType(TEXT_PLAIN); for(Extension extension : configuration.getUse()) { for(Kind kind : extension.getKinds()) { out.println(CATEGORY + ": " + asCategory(kind, true)); // response.addHeader(CATEGORY, asString(kind)); for(Action action : kind.getActions()) { out.println(CATEGORY + ": " + asString(action)); // response.addHeader(CATEGORY, asString(action)); } } for(Mixin mixin : extension.getMixins()) { out.println(CATEGORY + ": " + asCategory(mixin, true)); // response.addHeader(CATEGORY, asString(mixin)); for(Action action : mixin.getActions()) { out.println(CATEGORY + ": " + asString(action)); // response.addHeader(CATEGORY, asString(action)); } } } } private String asCategory(Kind kind, boolean detailed) { StringBuilder sb = new StringBuilder(); sb.append(kind.getTerm()) .append(";scheme=\"").append(kind.getScheme()).append("\";class=\"kind\""); if(detailed) { sb.append(";title=\"").append(kind.getTitle()).append('\"'); Kind parent = kind.getParent(); if(parent != null) { sb.append(";rel=\"").append(parent.getScheme()).append(parent.getTerm()).append('\"'); } sb.append(";location=\"").append(getLocation(kind)).append('\"'); appendAttributes(sb, kind.getAttributes()); appendActions(sb, kind.getActions()); } return sb.toString(); } private String asCategory(Mixin mixin, boolean detailed) { StringBuilder sb = new StringBuilder(); sb.append(mixin.getTerm()) .append(";scheme=\"").append(mixin.getScheme()).append("\";class=\"mixin\""); if(detailed) { sb.append(";title=\"").append(mixin.getTitle()).append('\"'); List<Mixin> mixins = mixin.getDepends(); if(mixins.size() != 0) { sb.append(";rel=\""); String sep = ""; for(Mixin md : mixins) { sb.append(sep).append(md.getScheme()).append(md.getTerm()); sep = " "; } sb.append('\"'); } sb.append(";location=\"").append(getLocation(mixin)).append('\"'); appendAttributes(sb, mixin.getAttributes()); appendActions(sb, mixin.getActions()); } return sb.toString(); } private String asString(Action action) { StringBuilder sb = new StringBuilder(); sb.append(action.getTerm()) .append(";scheme=\"").append(action.getScheme()).append("\";class=\"action\"") .append(";title=\"").append(action.getTitle()).append('\"'); appendAttributes(sb, action.getAttributes()); return sb.toString(); } private void appendAttributes(StringBuilder sb, List<Attribute> attributes) { if(attributes.size() != 0) { sb.append(";attributes=\""); String sep = ""; for(Attribute attribute : attributes) { sb.append(sep).append(attribute.getName()); if(attribute.isRequired() || !attribute.isMutable()) { sb.append('{'); if(!attribute.isMutable()) { sb.append("immutable"); if(attribute.isRequired()) { sb.append(' '); } } if(attribute.isRequired()) { sb.append("required"); } sb.append('}'); } sep = " "; } sb.append('\"'); } } private void appendActions(StringBuilder sb, List<Action> actions) { if(actions.size() != 0) { sb.append(";actions=\""); String sep = ""; for(Action action : actions) { sb.append(sep).append(action.getScheme()).append(action.getTerm()); sep = " "; } sb.append('\"'); } } public static final String CLASS_ACTION = "action"; public static final String CLASS_KIND = "kind"; public static final String CLASS_MIXIN = "mixin"; //regular expression groups public static final String GROUP_TERM = "term"; public static final String GROUP_SCHEME = "scheme"; public static final String GROUP_CLASS = "class"; public static final String GROUP_TITLE = "title"; public static final String GROUP_REL = "rel"; public static final String GROUP_LOCATION = "location"; public static final String GROUP_ATTRIBUTES = "attributes"; public static final String GROUP_ACTIONS = "actions"; public static final String GROUP_URI = "uri"; public static final String GROUP_SELF = "self"; public static final String GROUP_CATEGORY = "category"; //regular expressions public static final String REGEXP_LOALPHA = "[a-z]"; public static final String REGEXP_ALPHA = "[a-zA-Z]"; public static final String REGEXP_DIGIT = "[0-9]"; public static final String REGEXP_INT = REGEXP_DIGIT + "+"; public static final String REGEXP_FLOAT = REGEXP_INT + "\\." + REGEXP_INT; public static final String REGEXP_NUMBER = REGEXP_FLOAT + "|" + REGEXP_INT; public static final String REGEXP_BOOL = "\\b(?<!\\|)true(?!\\|)\\b|\\b(?<!\\|)false(?!\\|)\\b"; public static final String REGEXP_QUOTED_STRING = "([^\"\\\\]|\\.)*"; public static final String REGEXP_URI = "(?x-mi:([a-zA-Z][\\-+.a-zA-Z\\d]*):(?:((?:[\\-_.!~*'()a-zA-Z\\d;?:@&=+$,]|%[a-fA-F\\d]{2})(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)|(?:(?:\\/\\/(?:(?:(?:((?:[\\-_.!~*'()a-zA-Z\\d;:&=+$,]|%[a-fA-F\\d]{2})*)@)?(?:((?:(?:[a-zA-Z0-9\\-.]|%[0-9a-fA-F][0-9a-fA-F])+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\[(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:(?:[a-fA-F\\d]{1,4}:)*[a-fA-F\\d]{1,4})?::(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))?)\\]))(?::(\\d*))?))?|((?:[\\-_.!~*'()a-zA-Z\\d$,;:@&=+]|%[a-fA-F\\d]{2})+))|(?!\\/\\/))(\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?)(?:\\?((?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*))?)(?:\\#((?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*))?)"; public static final String REGEXP_URI_REF = "(?:[a-zA-Z][\\-+.a-zA-Z\\d]*:(?:(?:\\/\\/(?:(?:(?:[\\-_.!~*'()a-zA-Z\\d;:&=+$,]|%[a-fA-F\\d]{2})*@)?(?:(?:[a-zA-Z0-9\\-.]|%[0-9a-fA-F][0-9a-fA-F])+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\[(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:(?:[a-fA-F\\d]{1,4}:)*[a-fA-F\\d]{1,4})?::(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))?)\\])(?::\\d*)?|(?:[\\-_.!~*'()a-zA-Z\\d$,;:@&=+]|%[a-fA-F\\d]{2})+)(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?|\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)(?:\\?(?:(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*))?|(?:[\\-_.!~*'()a-zA-Z\\d;?:@&=+$,]|%[a-fA-F\\d]{2})(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)|(?:\\/\\/(?:(?:(?:[\\-_.!~*'()a-zA-Z\\d;:&=+$,]|%[a-fA-F\\d]{2})*@)?(?:(?:[a-zA-Z0-9\\-.]|%[0-9a-fA-F][0-9a-fA-F])+|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\[(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|(?:(?:[a-fA-F\\d]{1,4}:)*[a-fA-F\\d]{1,4})?::(?:(?:[a-fA-F\\d]{1,4}:)*(?:[a-fA-F\\d]{1,4}|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}))?)\\])(?::\\d*)?|(?:[\\-_.!~*'()a-zA-Z\\d$,;:@&=+]|%[a-fA-F\\d]{2})+)(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?|\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*|(?:[\\-_.!~*'()a-zA-Z\\d;@&=+$,]|%[a-fA-F\\d]{2})+(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*(?:\\/(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*(?:;(?:[\\-_.!~*'()a-zA-Z\\d:@&=+$,]|%[a-fA-F\\d]{2})*)*)*)?)(?:\\?(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)?)?(?:#(?:[\\-_.!~*'()a-zA-Z\\d;\\/?:@&=+$,\\[\\]]|%[a-fA-F\\d]{2})*)?"; public static final String REGEXP_TERM = "(" + REGEXP_ALPHA + "|" + REGEXP_DIGIT + ")(" + REGEXP_LOALPHA + "|" + REGEXP_DIGIT + "|-|_)*"; public static final String REGEXP_SCHEME = REGEXP_URI + "#"; public static final String REGEXP_TYPE_IDENTIFIER = REGEXP_SCHEME + REGEXP_TERM; public static final String REGEXP_CLASS = "\\b(?<!\\|)action(?!\\|)\\b|\\b(?<!\\|)mixin(?!\\|)\\b|\\b(?<!\\|)kind(?!\\|)\\b"; public static final String REGEXP_TYPE_IDENTIFIER_LIST = REGEXP_TYPE_IDENTIFIER + "(\\s+" + REGEXP_TYPE_IDENTIFIER + ")*"; public static final String REGEXP_ATTRIBUTE_COMPONENT = REGEXP_LOALPHA + "(" + REGEXP_LOALPHA + "|" + REGEXP_DIGIT + "|-|_)*"; public static final String REGEXP_ATTRIBUTE_NAME = REGEXP_ATTRIBUTE_COMPONENT + "(\\." + REGEXP_ATTRIBUTE_COMPONENT + ")*"; public static final String REGEXP_ATTRIBUTE_PROPERTIES = "\\{(?:required immutable|immutable required|required|immutable)\\}"; public static final String REGEXP_ATTRIBUTE_DEF = "(?:" + REGEXP_ATTRIBUTE_NAME + ")(?:" + REGEXP_ATTRIBUTE_PROPERTIES + ")?"; public static final String REGEXP_ATTRIBUTE_LIST = REGEXP_ATTRIBUTE_DEF + "(\\s+" + REGEXP_ATTRIBUTE_DEF + ")*"; public static final String REGEXP_ATTRIBUTE_REPR = REGEXP_ATTRIBUTE_NAME + "=(\"" + REGEXP_QUOTED_STRING + "\"|" + REGEXP_NUMBER + "|" + REGEXP_BOOL + ");?"; public static final String REGEXP_ACTION = REGEXP_TYPE_IDENTIFIER; public static final String REGEXP_ACTION_LIST = REGEXP_ACTION + "(\\s+" + REGEXP_ACTION + ")*"; public static final String REGEXP_RESOURCE_TYPE = REGEXP_TYPE_IDENTIFIER + "(\\s+" + REGEXP_TYPE_IDENTIFIER + ")*"; public static final String REGEXP_LINK_INSTANCE = REGEXP_URI_REF; public static final String REGEXP_LINK_TYPE = REGEXP_TYPE_IDENTIFIER + "(\\s+" + REGEXP_TYPE_IDENTIFIER + ")*"; public static final String REGEXP_CATEGORY = "(?<" + GROUP_TERM + ">" + REGEXP_TERM + ")" // term (mandatory) + ";\\s*scheme=\"(?<" + GROUP_SCHEME + ">" + REGEXP_SCHEME + ")(?:" + REGEXP_TERM + ")?\"" // scheme (mandatory) + ";\\s*class=\"?(?<" + GROUP_CLASS + ">" + REGEXP_CLASS + ")\"?" // class (mandatory) + "(;\\s*title=\"(?<" + GROUP_TITLE + ">" + REGEXP_QUOTED_STRING + ")\")?" // title (optional) + "(;\\s*rel=\"(?<" + GROUP_REL + ">" + REGEXP_TYPE_IDENTIFIER_LIST + ")\")?" // rel (optional) + "(;\\s*location=\"(?<" + GROUP_LOCATION + ">" + REGEXP_URI_REF + ")\")?" // location (optional) + "(;\\s*attributes=\"(?<" + GROUP_ATTRIBUTES + ">" + REGEXP_ATTRIBUTE_LIST + ")\")?" // attributes (optional) + "(;\\s*actions=\"(?<" + GROUP_ACTIONS + ">" + REGEXP_ACTION_LIST + ")\")?" // actions (optional) + ";?"; // additional semicolon at the end (not specified, for interoperability) public static final String REGEXP_ATTRIBUTES = "(" + REGEXP_ATTRIBUTE_DEF + ")"; public static final String REGEXP_LINK = "\\<(?<" + GROUP_URI + ">" + REGEXP_URI_REF + ")\\>" // uri (mandatory) + ";\\s*rel=\"(?<" + GROUP_REL + ">" + REGEXP_RESOURCE_TYPE + ")\"" // rel (mandatory) + "(;\\s*self=\"(?<" + GROUP_SELF + ">" + REGEXP_LINK_INSTANCE + ")\")?" // self (optional) + "(;\\s*category=\"(?<" + GROUP_CATEGORY + ">(;?\\s*(" + REGEXP_LINK_TYPE + "))+)\")?" // category (optional) + "(;\\s*(?<" + GROUP_ATTRIBUTES + ">(;?\\s*" + REGEXP_ATTRIBUTE_REPR + ")*))?" // attributes (optional) + ";?"; // additional semicolon at the end (not specified, for interoperability) public static final Pattern PATTERN_CATEGORY = Pattern.compile("Category: " + REGEXP_CATEGORY); public static final Pattern PATTERN_ATTRIBUTES = Pattern.compile(REGEXP_ATTRIBUTES); public static final Pattern PATTERN_LINK = Pattern.compile(REGEXP_LINK); }