View Javadoc
1   /**
2    * This file Copyright (c) 2012-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.api.location;
35  
36  import info.magnolia.context.Context;
37  import info.magnolia.context.MgnlContext;
38  import info.magnolia.context.WebContext;
39  
40  import java.io.UnsupportedEncodingException;
41  import java.net.URLDecoder;
42  import java.nio.charset.StandardCharsets;
43  import java.util.StringTokenizer;
44  
45  import org.apache.commons.lang3.StringUtils;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  /**
50   * Default location implementation. Follows the pattern: {@code appType:appName:subAppId;some/parameter}.
51   */
52  public class DefaultLocation implements Location {
53  
54      private static final String[] SPECIAL_CHARACTERS = new String[]{";", ":"};
55      private static final String[] SPECIAL_CHARACTERS_REPLACEMENT = new String[]{"[semi]", "[colon]"};
56  
57      private static Logger log = LoggerFactory.getLogger(DefaultLocation.class);
58  
59      private String appType;
60  
61      private String appName;
62  
63      private String subAppId;
64  
65      private String parameter;
66  
67      public DefaultLocation() {
68      }
69  
70      public DefaultLocation(String appType, String appName) {
71          this(appType, appName, "");
72      }
73  
74      public DefaultLocation(String appType, String appName, String subAppId) {
75          this(appType, appName, subAppId, "");
76      }
77  
78      public DefaultLocation(String appType, String appName, String subAppId, String parameter) {
79          this.appType = decodeFragment(appType);
80          this.appName = decodeFragment(appName);
81          this.subAppId = decodeFragment(subAppId);
82          this.parameter = decodeFragment(parameter);
83      }
84  
85      /**
86       * @throws IllegalArgumentException if the passed fragment is null or empty.
87       */
88      public DefaultLocation(String fragment) {
89          if (StringUtils.isBlank(fragment)) {
90              throw new IllegalArgumentException("Fragment cannot be empty or null");
91          }
92          parseLocation(fragment);
93      }
94  
95      private void parseLocation(String fragment) {
96          String[] split = StringUtils.split(fragment, ";");
97          setAppParams(split[0]);
98          this.parameter = split.length == 2 ? decodeFragment(split[1]) : "";
99      }
100 
101     private void setAppParams(String appParams) {
102         StringTokenizer tokenizer = new StringTokenizer(appParams, ":");
103         this.appType = decodeFragment((tokenizer.hasMoreTokens()) ? tokenizer.nextToken() : "");
104         this.appName = decodeFragment((tokenizer.hasMoreTokens()) ? tokenizer.nextToken() : "");
105         this.subAppId = decodeFragment((tokenizer.hasMoreTokens()) ? tokenizer.nextToken() : "");
106     }
107 
108     @Override
109     public String getAppType() {
110         return appType;
111     }
112 
113     @Override
114     public String getAppName() {
115         return appName;
116     }
117 
118     @Override
119     public String getSubAppId() {
120         return subAppId;
121     }
122 
123     @Override
124     public String getParameter() {
125         return parameter;
126     }
127 
128     public void setParameter(String parameter) {
129         this.parameter = decodeFragment(parameter);
130     }
131 
132     public void setSubAppId(String subAppId) {
133         this.subAppId = decodeFragment(subAppId);
134     }
135 
136     public void setAppType(String appType) {
137         this.appType = decodeFragment(appType);
138     }
139 
140     public void setAppName(String appName) {
141         this.appName = decodeFragment(appName);
142     }
143 
144     @Override
145     public boolean equals(Object o) {
146         if (this == o) {
147             return true;
148         }
149 
150         if ((o == null) || !(o instanceof  DefaultLocation)) {
151             return false;
152         }
153 
154         DefaultLocation./../../info/magnolia/ui/api/location/DefaultLocation.html#DefaultLocation">DefaultLocation that = (DefaultLocation) o;
155 
156         if (appType != null ? !appType.equals(that.appType) : that.appType != null) {
157             return false;
158         }
159         if (appName != null ? !appName.equals(that.appName) : that.appName != null) {
160             return false;
161         }
162         if (subAppId != null ? !subAppId.equals(that.subAppId) : that.subAppId != null) {
163             return false;
164         }
165         if (parameter != null ? !parameter.equals(that.parameter) : that.parameter != null) {
166             return false;
167         }
168 
169         return true;
170     }
171 
172     @Override
173     public int hashCode() {
174         int result = appType != null ? appType.hashCode() : 0;
175         result = 31 * result + (appName != null ? appName.hashCode() : 0);
176         result = 31 * result + (subAppId != null ? subAppId.hashCode() : 0);
177         result = 31 * result + (parameter != null ? parameter.hashCode() : 0);
178         return result;
179     }
180 
181     @Override
182     public String toString() {
183         StringBuilder sb = new StringBuilder();
184         if (appType != null && appType.length() != 0) {
185             sb.append(appType);
186             if (appName != null && appName.length() != 0) {
187                 sb.append(":").append(appName);
188             }
189             if (subAppId != null && subAppId.length() != 0) {
190                 sb.append(":").append(subAppId);
191             }
192             if (parameter != null && parameter.length() != 0) {
193                 sb.append(";").append(parameter);
194             }
195         }
196         return sb.toString();
197     }
198 
199     public static String extractAppType(String fragment) {
200         int i = fragment.indexOf(':');
201         return i != -1 ? fragment.substring(0, i) : fragment;
202     }
203 
204     public static String extractAppName(String fragment) {
205         fragment = removeParameter(fragment);
206         int i = fragment.indexOf(':');
207         if (i == -1) {
208             return "";
209         }
210         int j = fragment.indexOf(':', i + 1);
211         return j != -1 ? fragment.substring(i + 1, j) : fragment.substring(i + 1);
212     }
213 
214     public static String extractSubAppId(String fragment) {
215         fragment = removeParameter(fragment);
216 
217         int i = fragment.indexOf(':');
218         if (i == -1) {
219             return "";
220         }
221         int j = fragment.indexOf(':', i + 1);
222         if (j == -1) {
223             return "";
224         }
225         return fragment.substring(j + 1);
226     }
227 
228     public static String extractParameter(String fragment) {
229         int i = fragment.indexOf(';');
230         if (i == -1) {
231             return "";
232         }
233         return fragment.substring(i + 1);
234     }
235 
236     private static String removeParameter(String fragment) {
237         int i = fragment.indexOf(';');
238         return i != -1 ? fragment.substring(0, i) : fragment;
239     }
240 
241     /**
242      * Decodes <code>application/x-www-form-urlencoded</code> fragment string using a specified encoding scheme if necessary.
243      */
244     public static String decodeFragment(String fragment, String encoding) {
245         if (fragment == null) {
246             return fragment;
247         }
248 
249         if (fragment.indexOf('%') > -1) {
250             try {
251                 fragment = URLDecoder.decode(fragment, encoding);
252             } catch (UnsupportedEncodingException e) {
253                 log.error("Error decoding fragment '" + fragment + "' with encoding '" + encoding + "'", e);
254             }
255         }
256 
257         return fragment;
258     }
259 
260     /**
261      * Decodes a fragment using the character encoding from the {@link info.magnolia.cms.core.AggregationState}.
262      */
263     public static String decodeFragment(String fragment) {
264         Context context = MgnlContext.hasInstance() ? MgnlContext.getInstance() : null;
265         String encoding = StandardCharsets.UTF_8.name();
266         if (context instanceof WebContext) {
267             encoding = MgnlContext.getAggregationState().getCharacterEncoding();
268         }
269         return decodeFragment(fragment, encoding);
270     }
271 
272     protected String escapeSpecialCharacters(String toEscape) {
273         return StringUtils.replaceEach(toEscape, SPECIAL_CHARACTERS, SPECIAL_CHARACTERS_REPLACEMENT);
274     }
275 
276     protected String unescapeSpecialCharacters(String toUnescape) {
277         return StringUtils.replaceEach(toUnescape, SPECIAL_CHARACTERS_REPLACEMENT, SPECIAL_CHARACTERS);
278     }
279 
280 }