/** Load configuration MACM Configuration */
  public void init() {
    String username =
        WLServerAPIProvider.getWLServerAPI()
            .getConfigurationAPI()
            .getMFPConfigurationProperty("ibm.macm.username");
    String password =
        WLServerAPIProvider.getWLServerAPI()
            .getConfigurationAPI()
            .getMFPConfigurationProperty("ibm.macm.password");

    CREDENTIALS =
        "Basic " + new String(Base64.encodeBase64((username + ":" + password).getBytes()));
    SERVER =
        WLServerAPIProvider.getWLServerAPI()
            .getConfigurationAPI()
            .getMFPConfigurationProperty("ibm.macm.serverurl");

    if (SERVER.startsWith("https")) {
      relaxHostChecking();
    }
  }
@Path("/")
@OAuthSecurity(enabled = false)
public class CaaSResource {

  public static String CREDENTIALS;
  public static String SERVER;
  public static String TENANT;
  private static List<String> COOKIES;
  private HttpURLConnection urlConnection;
  protected boolean includeTypeInformation = false;

  /*
   * For more info on JAX-RS see
   * https://jsr311.java.net/nonav/releases/1.1/index.html
   */

  // Define logger (Standard java.util.Logger)
  static Logger logger = Logger.getLogger(CaaSResource.class.getName());

  // Define the server api to be able to perform server operations
  WLServerAPI api = WLServerAPIProvider.getWLServerAPI();

  /** Load configuration MACM Configuration */
  public void init() {
    String username =
        WLServerAPIProvider.getWLServerAPI()
            .getConfigurationAPI()
            .getMFPConfigurationProperty("ibm.macm.username");
    String password =
        WLServerAPIProvider.getWLServerAPI()
            .getConfigurationAPI()
            .getMFPConfigurationProperty("ibm.macm.password");

    CREDENTIALS =
        "Basic " + new String(Base64.encodeBase64((username + ":" + password).getBytes()));
    SERVER =
        WLServerAPIProvider.getWLServerAPI()
            .getConfigurationAPI()
            .getMFPConfigurationProperty("ibm.macm.serverurl");

    if (SERVER.startsWith("https")) {
      relaxHostChecking();
    }
  }

  /** Pass throughout CERTs [workaround] */
  public void relaxHostChecking() {

    // Override SSL Trust manager without certificate chains validation
    TrustManager[] trustAllCerts =
        new TrustManager[] {
          new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
              return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {}

            public void checkServerTrusted(X509Certificate[] certs, String authType) {}
          }
        };

    try {
      SSLContext sc = SSLContext.getInstance("SSL");
      sc.init(null, trustAllCerts, new java.security.SecureRandom());
      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

      // Hostname verification.
      HostnameVerifier allHostsValid =
          new HostnameVerifier() {
            /**
             * Verify that the host name is an acceptable match with the server's authentication
             * scheme.
             *
             * @hostname - the host name
             * @session - SSLSession used on the connection to host
             * @return true if the host name is acceptable
             */
            public boolean verify(String hostname, SSLSession session) {
              return true;
            }
          };
      // Sets the default HostnameVerifier by all-trusting host verifier.
      HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);

    } catch (NoSuchAlgorithmException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (KeyManagementException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  /**
   * Logs into the CaaS system.
   *
   * @return {@link Integer} status code -- equal 201 if success
   */
  public Integer connect() throws URISyntaxException, IOException {
    // log message to server log
    logger.info(this.getClass().getSimpleName() + " Connect to MACM INSTANCE / Basic Auth...");
    init();

    try {

      URL url = null;
      if (TENANT == null) {

        url =
            new URL(
                SERVER
                    + InternalConstants.SERVER_DEFAULT_CONTEXT_ROOT_MY_CONTENTHANDLER
                    + "?"
                    + InternalConstants.CAAS_QUERY_PARAMETER_BASICAUTH);

      } else {
        url =
            new URL(
                SERVER
                    + InternalConstants.SERVER_DEFAULT_CONTEXT_ROOT_MY_CONTENTHANDLER
                    + "/"
                    + TENANT
                    + "?"
                    + InternalConstants.CAAS_QUERY_PARAMETER_BASICAUTH);
      }

      urlConnection = (HttpURLConnection) url.openConnection();
      urlConnection.setRequestMethod("POST");
      urlConnection.setRequestProperty("Authorization", CREDENTIALS);

    } catch (MalformedURLException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    int statusCode = urlConnection.getResponseCode();

    if (statusCode == 201 || statusCode == 204) {
      setCookies(urlConnection.getHeaderFields().get("Set-Cookie"));
    }
    return statusCode;
  }

  /**
   * Fetch content from MACM instance by doing a HTTP GET request and set Cookies
   *
   * @param url {@link String} MACM content url
   * @return {@link InputStream}
   */
  public InputStream openStream(String url) throws IOException {

    urlConnection = (HttpURLConnection) new URL(url).openConnection();
    urlConnection.setRequestMethod("GET");
    urlConnection.setUseCaches(false);

    HttpURLConnection.setFollowRedirects(true);

    if (COOKIES != null) {
      for (String cookie : CaaSResource.COOKIES) {
        urlConnection.addRequestProperty("Cookie", cookie.split(";", 1)[0]);
      }
    }

    if (urlConnection.getResponseCode() == 401 || urlConnection.getResponseCode() == 400) {
      try {
        connect();
      } catch (URISyntaxException e) {
        e.printStackTrace();
      } finally {
        openStream(url);
      }
    }

    return urlConnection.getInputStream();
  }

  /**
   * Returns a list of all content of the given type in the system.
   *
   * @param tenant (@link String) Root of the MACM instance
   * @param path (@link String) contentType - Acceptable values will vary depending on the types
   *     defined by the specific portal. Current values are: ["Articles", "Biographies", "Catalog",
   *     "Notification", "Offerings", "Plain Texts", "Rich Texts"]
   * @param oid (@link String) Unique categorie identifier (Ex: 73959b0d-7e87-44d9-b6ca-5ef7343bc545
   *     )
   * @return an {@link Object} list of items and names of fields { names: { title: 0, id: 1, ... }
   *     values: [ ["title", "id value", ... ] }
   *     <p>Path for method: "<server
   *     address>/CAAS_Hybrid/adapters/CaaS/items?type={type}&lib={lib-name}" "<server
   *     address>/CAAS_Hybrid/adapters/CaaS/items?oid={oid}"
   *     <p>for instance "<server address>/CAAS_Hybrid/adapters/CaaS/items?type=Offer&lib=MACM
   *     Default Application"
   * @throws Exception
   */
  @GET
  @Path("/items")
  @Produces(javax.ws.rs.core.MediaType.APPLICATION_JSON)
  public String CAASContentItemsRequest(
      @QueryParam("tenant") String tenant,
      @QueryParam("type") String type,
      @QueryParam("oid") String oid,
      @QueryParam("pageSize") int pageSize,
      @QueryParam("pageNumber") int pageNumber,
      @QueryParam("categories.all") String categoriesall,
      @QueryParam("keywords.all") String keywordsall,
      @QueryParam("categories.any") String categoriesany,
      @QueryParam("keywords.any") String keywordsany,
      @QueryParam("titleFilter") String titleFilter,
      @QueryParam("workflowstatus") String workflowstatus,
      @QueryParam("sortcriteria") String sortcriteria,
      @QueryParam("property") String propertyKeys,
      @QueryParam("element") String elementKeys,
      @QueryParam("lib") String libName,
      @QueryParam("created.since") String csince,
      @QueryParam("created.before") String cbefore,
      @QueryParam("modified.since") String msince,
      @QueryParam("modified.before") String mbefore)
      throws Exception {
    if (SERVER == null) {
      init();
    }

    String source = SERVER + InternalConstants.SERVER_CONTEXT_ROOT;

    if (tenant != null) {
      TENANT = tenant;
      source += "/" + tenant + "/caas";
    } else {
      source += "/caas";
    }

    String categories = null;
    FilterBy fCategoriesType = null;
    String keywords = null;
    FilterBy fKeywordsType = null;

    if (categoriesall != null) {

      categories = categoriesall;
      fCategoriesType = FilterBy.ALL;

    } else if (categoriesany != null) {
      categories = categoriesany;
      fCategoriesType = FilterBy.ANY;
    } else {
      categories = null;
      fCategoriesType = null;
    }

    if (keywordsall != null) {
      keywords = keywordsall;
      fKeywordsType = FilterBy.ALL;

    } else if (keywordsany != null) {
      keywords = keywordsany;
      fKeywordsType = FilterBy.ANY;
    } else {
      keywords = null;
      fKeywordsType = null;
    }

    if ((oid != null)) {
      source +=
          buildQuery(
              RequestBy.ID,
              oid,
              pageSize,
              pageNumber,
              sortcriteria,
              fCategoriesType,
              categories,
              fKeywordsType,
              keywords,
              titleFilter,
              workflowstatus,
              elementKeys,
              propertyKeys,
              libName,
              csince,
              cbefore,
              msince,
              mbefore);

    } else {
      if (type != null) {
        source +=
            buildQuery(
                RequestBy.PATH,
                type,
                pageSize,
                pageNumber,
                sortcriteria,
                fCategoriesType,
                categories,
                fKeywordsType,
                keywords,
                titleFilter,
                workflowstatus,
                elementKeys,
                propertyKeys,
                libName,
                csince,
                cbefore,
                msince,
                mbefore);
      } else {
        return null;
      }
    }

    return jsonFormatter(IOUtils.toString(openStream(source)), true).toString(4);
  }

  JSONObject jsonFormatter(String content, Boolean isCategorie) throws JSONException {

    JSONObject mData = new JSONObject(content);

    JSONObject header = new JSONObject(mData.opt("header"));
    Map<Integer, String> propertyIndexes, elementIndexes;
    Map<String, Object> itemsObject;
    List<Map<String, Object>> items = new ArrayList<>();

    elementIndexes = parseIndexes(header, "elementIndex");
    propertyIndexes = parseIndexes(header, "propertyIndex");

    JSONArray values = mData.optJSONArray("values");

    for (int i = 0; i < values.length(); i++) {
      JSONArray value = values.getJSONArray(i);

      itemsObject = new HashMap<String, Object>();

      Map<String, Object> propertiesObject = new HashMap<String, Object>();

      for (int j = 0; j < value.length(); j++) {

        if (propertyIndexes.containsKey(j)) {
          itemsObject.put(propertyIndexes.get(j), value.optString(j));

        } else if (elementIndexes.containsKey(j)) {

          propertiesObject.put(elementIndexes.get(j), value.optString(j));
        } else {
          break;
        }

        if (!elementIndexes.isEmpty()) {
          itemsObject.put("properties", propertiesObject);
        }
      }

      items.add(itemsObject);
    }

    String listofProperties = mData.optString("listProperties");
    JSONObject tmp;
    if (listofProperties == null || listofProperties.isEmpty()) {
      tmp = new JSONObject();
    } else {
      tmp = new JSONObject(listofProperties);
    }
    if (isCategorie) {
      tmp.putOnce("items", new JSONArray(items));
    } else {

      tmp.putOnce("item", new JSONArray(items));
    }

    return tmp;
  }

  Map<Integer, String> parseIndexes(JSONObject header, String indexesName) throws JSONException {
    Map<Integer, String> map = new HashMap<Integer, String>();
    if (header.has(indexesName)) {
      JSONObject json = header.getJSONObject(indexesName);
      @SuppressWarnings("unchecked")
      Iterator<String> it = json.keys();
      while (it.hasNext()) {
        String name = it.next();
        map.put(json.getInt(name), name);
      }
    }
    return map;
  }

  /**
   * Returns a single piece of content
   *
   * @param tenant (@link String) Root of the MACM instance
   * @param path (@link String) Path of requested content - /Offer/Offer 1
   * @param oid (@link String) ID of requested content
   * @return (@link Object) of requested content { names: { title: 0, id: 1, ... } values: [
   *     ["title", "id value", ... ] }
   *     <p>Path for method: "<server address>/CAAS_Hybrid/adapters/CaaS/item?path={path}" "<server
   *     address>/CAAS_Hybrid/adapters/CaaS/item?oid={oid}"
   * @throws Exception
   */
  @GET
  @Path("/item")
  @Produces(javax.ws.rs.core.MediaType.APPLICATION_JSON)
  public String CAASContentItemRequest(
      @QueryParam("tenant") String tenant,
      @QueryParam("type") String type,
      @QueryParam("oid") String oid,
      @QueryParam("lib") String libName)
      throws Exception {
    if (SERVER == null) {
      init();
    }

    String source = SERVER + InternalConstants.SERVER_CONTEXT_ROOT;

    if (tenant != null) {
      TENANT = tenant;
      source += "/" + tenant + "/caas";
    } else {
      source += "/caas";
    }

    if ((oid != null)) {
      source +=
          buildQuery(
              RequestBy.ID,
              oid,
              0,
              0,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null,
              null);

    } else {

      if (type != null) {
        source +=
            buildQuery(
                RequestBy.PATH,
                type,
                0,
                0,
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                null,
                libName,
                null,
                null,
                null,
                null);
      } else {
        return null;
      }
    }

    return jsonFormatter(IOUtils.toString(openStream(source)), false).toString(4);
  }

  /**
   * Retrieves the asset at the given URL on the CaaS server and returns it in Base64
   *
   * @param (@link String) assetURL
   * @return (@link Response) base64 encoded image
   *     <p>Path for method: "<server address>/CAAS_Hybrid/adapters/CaaS/asset?assetURL={assetURL}"
   */
  @GET
  @Path("/asset")
  public Response CAASAssetRequest(@QueryParam("assetURL") String assetURL)
      throws IOException, URISyntaxException {

    if (SERVER == null) {
      connect();
    }
    return Response.ok(
            new ByteArrayInputStream(IOUtils.toByteArray(openStream(SERVER + assetURL))),
            urlConnection.getContentType())
        .build();
  }

  /**
   * Retrieves all openned projects
   *
   * @return (@link Response) List of open projects
   * @throws Exception
   */
  @GET
  @Path("/projects")
  public String CAASOpenProjectsRequest() throws Exception {

    if (SERVER == null) {
      connect();
    }

    String source = SERVER + InternalConstants.SERVER_CONTEXT_ROOT;
    source +=
        buildQuery(
            RequestBy.URL,
            null,
            0,
            0,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            null,
            InternalConstants.CAAS_OPEN_PROJECTS,
            null,
            null,
            null,
            null);

    return jsonFormatter(IOUtils.toString(openStream(source)), true).toString(4);
  }

  /**
   * Set the Cookies's List
   *
   * @param cookies (List<String>) Requested cookies
   */
  public void setCookies(List<String> cookies) {
    CaaSResource.COOKIES = cookies;
  }

  public enum RequestBy {
    /** Indicates a request where the content is obtained by specifiying its content id. */
    ID,
    /** Indicates a request where the content is obtained by specifiying its content path. */
    PATH,
    /** Indicates a request where the content is obtained by specifiying a URL. */
    URL
  }

  public enum CriteriaEnum {
    lastmodifieddate,
    creationdate,
    position,
    title,
    author,
    expirydate,
    publishdate,
    status
  }

  public enum FilterBy {
    ALL,
    ANY
  }

  /**
   * The identifier for the content retrived by this request. Its meaning dependens on the type of
   * request: a single content a item, a list of content items or an image URL.
   */
  String buildQuery(
      RequestBy contentIdentifierType,
      String contentIdentifier,
      int pageSize,
      int pageNumber,
      /* Map<String, Boolean> */ String sortCriteria,
      FilterBy fCategorieType,
      String categoryFilter,
      FilterBy fKeywordsType,
      String keywordFilter,
      String titleFilter,
      String workflowstatus,
      String elementKeys,
      String propertyKeys,
      String libName,
      String csince,
      String cbefore,
      String msince,
      String mbefore)
      throws Exception {
    StringBuilder sb = new StringBuilder();
    sb.append('?')
        .append(
            encodeParam(
                InternalConstants.CAAS_QUERY_PARAMETER_MIME_TYPE,
                InternalConstants.CAAS_QUERY_PARAMETER_MIME_TYPE_JSON));
    sb.append('&')
        .append(
            encodeParam(
                InternalConstants.CAAS_QUERY_PARAMETER_PAGE,
                InternalConstants.CAAS_QUERY_PARAMETER_PAGE_DEFAULT));
    if (contentIdentifierType == RequestBy.ID) {
      sb.append('&')
          .append(
              encodeParam(
                  InternalConstants.CAAS_QUERY_PARAMETER_URILE,
                  InternalConstants.CAAS_URILE_BY_ID + contentIdentifier));
    } else if (contentIdentifierType == RequestBy.PATH) {
      sb.append('&')
          .append(
              encodeParam(
                  InternalConstants.CAAS_QUERY_PARAMETER_URILE,
                  InternalConstants.CAAS_URILE_BY_PATH
                      + libName
                      + InternalConstants.CAAS_CONTENT_TYPE_DEFAULT
                      + contentIdentifier));
    } else {
      sb.append('&')
          .append(
              encodeParam(
                  InternalConstants.CAAS_QUERY_PARAMETER_URILE,
                  InternalConstants.CAAS_URILE_BY_PATH + InternalConstants.CAAS_OPEN_PROJECTS))
          .append('&')
          .append(encodeParam("current", true));
    }

    if (pageSize != 0) {
      sb.append('&').append(encodeParam("ibm.pageSize", pageSize));
    }
    if (pageNumber != 0) {
      sb.append('&').append(encodeParam("ibm.pageNumber", pageNumber));
    }
    if (sortCriteria != null) {
      sb.append('&').append(encodeParam("ibm.sortcriteria", sortCriteria));
    }
    if (categoryFilter != null) {

      if (fCategorieType != null) {
        if (fCategorieType == FilterBy.ALL) {
          sb.append('&').append(encodeParam("ibm.filter.categories.all", categoryFilter));
        } else {
          sb.append('&').append(encodeParam("ibm.filter.categories.any", categoryFilter));
        }
      }
    }
    if (keywordFilter != null) {
      if (fKeywordsType != null) {
        if (fKeywordsType == FilterBy.ALL) {
          sb.append('&').append(encodeParam("ibm.filter.keywords.all", keywordFilter));

        } else {
          sb.append('&').append(encodeParam("ibm.filter.keywords.any", keywordFilter));
        }
      }
    }
    if (titleFilter != null) {
      sb.append('&').append(encodeParam("ibm.filter.title", titleFilter));
    }
    if (workflowstatus != null) {
      sb.append('&').append(encodeParam("ibm.filter.workflowstatus", workflowstatus));
    }

    if (elementKeys != null) {
      sb.append('&').append(encodeParam("ibm.element.keys", elementKeys));
    }

    if (propertyKeys != null) {
      sb.append('&').append(encodeParam("ibm.property.keys", propertyKeys));
    }

    if (csince != null) {
      sb.append('&').append(encodeParam("ibm.filter.created.since", csince));
    }

    if (cbefore != null) {
      sb.append('&').append(encodeParam("ibm.filter.created.before", cbefore));
    }
    if (msince != null) {
      sb.append('&').append(encodeParam("ibm.filter.modified.since", msince));
    }

    if (mbefore != null) {
      sb.append('&').append(encodeParam("ibm.filter.modified.before", mbefore));
    }

    if (includeTypeInformation) {
      sb.append('&').append(encodeParam("ibm.type.information", "true"));
    }
    return sb.toString();
  }

  String encodeParam(String key, Object value) throws Exception {
    StringBuilder sb =
        new StringBuilder(URLEncoder.encode(key, InternalConstants.UTF_8).replace("+", "%20"));
    sb.append('=')
        .append(
            value == null
                ? ""
                : URLEncoder.encode(value.toString(), InternalConstants.UTF_8).replace("+", "%20"));
    return sb.toString();
  }

  /** This class is package-protected so as not to be exposed to clients. */
  static class InternalConstants {

    // Logging stuff
    static final String LOG_ENTRY = " ENTRY ";
    static final String LOG_EXIT = " EXIT ";

    // Android system properties
    static final String PROPERTY_HTTP_AGENT = "http.agent";

    // HTTP connection stuff
    static final String HTTP_SCHEME_HTTP = "http";
    static final String HTTP_SCHEME_HTTPS = "https";
    static final String HTTP_HEADER_USER_AGENT = "User-Agent";
    static final String HTTP_HEADER_ACCEPT_LANGUAGE = "Accept-Language";

    static final String HTTP_HEADER_FIELD_CAAS_RESPONSE = "caas-response";

    // Server connection stuff
    static final String SERVER_DEFAULT_SCHEME = HTTP_SCHEME_HTTP;
    static final String SERVER_DEFAULT_PORT = "80";
    static final String SERVER_DEFAULT_CONTEXT_ROOT = "/wps/mypoc";
    static final String SERVER_CONTEXT_ROOT = "/wps/myportal";
    static final String SERVER_DEFAULT_INSTANCE = "";
    static final String SERVER_DEFAULT_CONTEXT_ROOT_MY_CONTENTHANDLER = "/wps/mycontenthandler";
    static final String SERVER_DEFAULT_PROJECT = "$project";

    static final String SERVER_DEFAULT_QUERY_URI_JSECURITY_CHECK =
        "!ut/p/model/service-document/j_security_check";
    static final String SERVER_DEFAULT_QUERY_URI_PARAMETER_J_USERNAME = "******";
    static final String SERVER_DEFAULT_QUERY_URI_PARAMETER_J_PASSWORD = "******";

    // Encoding stuff
    static final String DEFAULT_ENCODING = "UTF-8";

    // CaaS stuff
    static final String CAAS_QUERY_PARAMETER_URILE = "urile";

    static final String CAAS_QUERY_PARAMETER_BASICAUTH = "uri=login:basicauth";

    static final String CAAS_URILE_BY_ID = "wcm:oid:";
    static final String CAAS_URILE_BY_PATH = "wcm:path:";
    static final String CAAS_QUERY_PARAMETER_PAGE = "page";
    static final String CAAS_QUERY_PARAMETER_MIME_TYPE = "mime-type";
    static final String CAAS_QUERY_PARAMETER_PAGE_DEFAULT = "ibm.portal.caas.page";
    static final String CAAS_CONTENT_TYPE_DEFAULT = "/Content Types/";
    static final String CAAS_OPEN_PROJECTS = "MACM System/Views/Open Projects";

    static final String CAAS_QUERY_PARAMETER_MIME_TYPE_JSON = "application/json";
    static final String CAAS_QUERY_PARAMETER_MIME_TYPE_XML = "application/xml";
    static final String CAAS_QUERY_PARAMETER_MIME_TYPE_DEFAULT =
        CAAS_QUERY_PARAMETER_MIME_TYPE_JSON;

    // Response handling
    static final String CAAS_RESPONSE_JSON_PREFIX_IBM = "ibm.portal.caas";
    static final String CAAS_RESPONSE_JSON_TITLE = "title";
    static final String CAAS_RESPONSE_JSON_ID = "id";
    static final String CAAS_RESPONSE_JSON_AUTHORS = "authors";
    static final String CAAS_RESPONSE_JSON_LAST_MODIFIED = "lastmodifieddate";

    /** The UTF-8 charset string. */
    static final String UTF_8 = "utf-8";
  }

  static class Constants {
    /** Default orders. */
    public enum Order {
      ASC,
      DESC;
    }

    public static final String CONTEXT_KEY_ACCEPT_LANGUAGES = "Accept-Language";
    public static final String CONTEXT_VALUE_ACCEPT_LANGUAGES_DEFAULT = "DEFAULT";
    public static final String CONTEXT_KEY_USER_AGENT = "User-Agent";
    public static final String CONTEXT_VALUE_USER_AGENT_DEFAULT = "DEFAULT";
    public static final String CONTEXT_KEY_INDEX_START = "index-start";
    public static final String CONTEXT_KEY_INDEX_COUNT = "index-count";
    public static final String CONTEXT_KEY_ORDER_CRITERIA = "order-criteria";
    public static final String CONTEXT_KEY_ORDER = "order";
    public static final String CONTEXT_VALUE_ORDER_DEFAULT = "DEFAULT"; // equals
    // Order.ASC
    public static final String CONTEXT_KEY_LOCATION = "location";
    public static final String CONTEXT_VALUE_LOCATION_DEFAULT = "DEFAULT"; // equals
    // current
    // location

    public static final String DATA_KEY_ID = "id";
    public static final String DATA_KEY_TITLE = "title";
  }
}