001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.lenya.cms.publication;
020:
021: import org.apache.avalon.framework.logger.AbstractLogEnabled;
022: import org.apache.avalon.framework.service.ServiceManager;
023: import org.apache.avalon.framework.service.Serviceable;
024: import org.apache.avalon.framework.thread.ThreadSafe;
025: import org.apache.lenya.cms.site.SiteNode;
026:
027: /**
028: * Default document builder implementation.
029: *
030: * @version $Id: DefaultDocumentBuilder.java 507914 2007-02-15 12:15:30Z andreas $
031: */
032: public class DefaultDocumentBuilder extends AbstractLogEnabled
033: implements DocumentBuilder, Serviceable, ThreadSafe {
034:
035: /**
036: * Ctor.
037: */
038: public DefaultDocumentBuilder() {
039: }
040:
041: /**
042: * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
043: */
044: public void service(ServiceManager manager) {
045: this .manager = manager;
046: }
047:
048: protected ServiceManager manager;
049:
050: /**
051: * Removes all "."-separated extensions from a URL (e.g.,
052: * <code>/foo.print.html</code> is transformed to <code>/foo</code>).
053: * @param url The URL to trim.
054: * @return A URL string.
055: */
056: protected String removeExtensions(String url) {
057: int dotIndex = url.indexOf(".");
058: if (dotIndex > -1) {
059: url = url.substring(0, dotIndex);
060: }
061: return url;
062: }
063:
064: /**
065: * Returns the language of a URL.
066: * @param urlWithoutSuffix The URL without the suffix.
067: * @return A string.
068: */
069: protected String getLanguage(String urlWithoutSuffix) {
070:
071: String language = "";
072: String url = urlWithoutSuffix;
073:
074: int languageSeparatorIndex = url.lastIndexOf("_");
075: if (languageSeparatorIndex > -1) {
076: String suffix = url.substring(languageSeparatorIndex + 1);
077: if (suffix.length() <= 5) {
078: language = suffix;
079: }
080: }
081: return language;
082: }
083:
084: /**
085: * Returns the extension of a URL.
086: * @param url The URL.
087: * @return The extension.
088: */
089: protected String getExtension(String url) {
090: int startOfSuffix = url.lastIndexOf('.');
091: String suffix = "";
092:
093: if ((startOfSuffix > -1) && !url.endsWith(".")) {
094: suffix = url.substring(startOfSuffix + 1);
095: }
096:
097: return suffix;
098: }
099:
100: /**
101: * @see org.apache.lenya.cms.publication.DocumentBuilder#isDocument(DocumentFactory,
102: * String)
103: */
104: public boolean isDocument(DocumentFactory factory, String url)
105: throws DocumentBuildException {
106: try {
107: DocumentLocator locator = getLocatorWithoutCheck(factory,
108: url);
109: if (locator != null) {
110: Publication pub = factory.getPublication(locator
111: .getPublicationId());
112: String path = locator.getPath();
113: Area area = pub.getArea(locator.getArea());
114: if (area.getSite().contains(path)) {
115: SiteNode node = area.getSite().getNode(path);
116: if (node.hasLink(locator.getLanguage())) {
117: return true;
118: }
119: }
120: }
121: } catch (Exception e) {
122: throw new DocumentBuildException(e);
123: }
124:
125: return false;
126: }
127:
128: /**
129: * Builds the canonical document URL.
130: * @param factory The document factory.
131: * @param locator The document locator.
132: * @return A string.
133: */
134: protected String buildCanonicalDocumentUrl(DocumentFactory factory,
135: DocumentLocator locator) {
136:
137: String languageSuffix = "";
138: String language = locator.getLanguage();
139:
140: Publication pub;
141: try {
142: pub = factory.getPublication(locator.getPublicationId());
143: } catch (PublicationException e) {
144: throw new RuntimeException(e);
145: }
146:
147: if (!language.equals(pub.getDefaultLanguage())) {
148: languageSuffix = "_" + language;
149: }
150:
151: String url = locator.getPath() + languageSuffix + ".html";
152: return url;
153: }
154:
155: public String buildCanonicalUrl(DocumentFactory factory,
156: DocumentLocator doc) {
157:
158: String documentUrl = buildCanonicalDocumentUrl(factory, doc);
159: String url = "/" + doc.getPublicationId() + "/" + doc.getArea()
160: + documentUrl;
161: return url;
162: }
163:
164: public DocumentLocator getLocator(DocumentFactory factory,
165: String webappUrl) throws DocumentBuildException {
166:
167: DocumentLocator locator = getLocatorWithoutCheck(factory,
168: webappUrl);
169: if (locator == null) {
170: throw new DocumentBuildException("The webapp URL ["
171: + webappUrl + "] does not refer to a document!");
172: }
173: return locator;
174: }
175:
176: /**
177: * Creates a document locator for a webapp URL without checking if the
178: * webapp URL refers to a locator first.
179: * @param factory The document factory.
180: * @param webappUrl The webapp URL.
181: * @return A document locator or <code>null</code> if the URL doesn't
182: * refer to a locator.
183: * @throws DocumentBuildException if an error occurs.
184: */
185: protected DocumentLocator getLocatorWithoutCheck(
186: DocumentFactory factory, String webappUrl)
187: throws DocumentBuildException {
188:
189: if (!webappUrl.startsWith("/")) {
190: return null;
191: }
192: if (webappUrl.substring(1).split("/").length < 3) {
193: return null;
194: }
195:
196: URLInformation info = new URLInformation(webappUrl);
197:
198: Publication publication;
199: try {
200: publication = PublicationUtil.getPublicationFromUrl(
201: this .manager, factory, webappUrl);
202: } catch (PublicationException e) {
203: throw new DocumentBuildException(e);
204: }
205:
206: String documentURL = info.getDocumentUrl();
207:
208: documentURL = removeExtensions(documentURL);
209:
210: String language = getLanguage(documentURL);
211: String fullLanguage = "".equals(language) ? ""
212: : ("_" + language);
213: documentURL = documentURL.substring(0, documentURL.length()
214: - fullLanguage.length());
215:
216: if ("".equals(language)) {
217: language = publication.getDefaultLanguage();
218: }
219:
220: String path = documentURL;
221:
222: if (!path.startsWith("/")) {
223: throw new DocumentBuildException("Path [" + path
224: + "] does not start with '/'!");
225: }
226:
227: return DocumentLocator.getLocator(publication.getId(), info
228: .getArea(), path, language);
229: }
230:
231: /**
232: * @see org.apache.lenya.cms.publication.DocumentBuilder#isValidDocumentName(java.lang.String)
233: */
234: public boolean isValidDocumentName(String documentName) {
235: return documentName.matches("[a-zA-Z0-9\\-]+");
236: }
237:
238: }
|