001: /*
002: * File : $Source: /usr/local/cvs/opencms/src-modules/org/opencms/workplace/tools/content/CmsTagReplaceSettings.java,v $
003: * Date : $Date: 2008-02-27 12:05:36 $
004: * Version: $Revision: 1.4 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: */
031:
032: package org.opencms.workplace.tools.content;
033:
034: import org.opencms.file.CmsObject;
035: import org.opencms.i18n.CmsMessageContainer;
036: import org.opencms.main.CmsIllegalArgumentException;
037: import org.opencms.main.CmsLog;
038: import org.opencms.util.CmsStringUtil;
039:
040: import java.util.Comparator;
041: import java.util.Iterator;
042: import java.util.List;
043: import java.util.Map;
044: import java.util.Set;
045: import java.util.SortedMap;
046: import java.util.TreeMap;
047: import java.util.TreeSet;
048: import java.util.Vector;
049:
050: import org.apache.commons.logging.Log;
051:
052: import org.htmlparser.Attribute;
053: import org.htmlparser.NodeFactory;
054: import org.htmlparser.PrototypicalNodeFactory;
055: import org.htmlparser.Tag;
056: import org.htmlparser.util.ParserException;
057:
058: /**
059: * Bean to hold the settings needed for the operation of replacing HTML Tags of xmlpage resources in
060: * the OpenCms VFS.
061: * <p>
062: *
063: * @author Achim Westermann
064: *
065: * @version $Revision: 1.4 $
066: *
067: * @since 6.1.7
068: *
069: */
070: public final class CmsTagReplaceSettings {
071:
072: /**
073: * Property for the tag-replace contentool to know the files that have been processed before in
074: * case of early terminaton in previous runs.
075: */
076: public static final String PROPERTY_CONTENTOOLS_TAGREPLACE = "contentools.tagreplace";
077:
078: /** The log object for this class. */
079: private static final Log LOG = CmsLog
080: .getLog(CmsTagReplaceSettings.class);
081:
082: /** Needed to verify if a path String denotes a folder in VFS. */
083: private final CmsObject m_cms;
084:
085: /** The tags that should be deleted. */
086: private final Set m_deleteTags;
087:
088: /** Used to create Tag instances for tags to delete of the proper type in a convenient way. */
089: private NodeFactory m_nodeFactory;
090:
091: /**
092: * The value of the shared {@link #PROPERTY_CONTENTOOLS_TAGREPLACE} to set on resources that
093: * have been processed with these settings.
094: */
095: private String m_propertyValueTagReplaceID;
096:
097: /**
098: * A map containing lower case tag names of tags to replace as keys and the replacement tag
099: * names as their corresponding values.
100: */
101: private SortedMap m_tags2replacementTags;
102:
103: /** The root of all content files to process. */
104: private String m_workPath;
105:
106: /**
107: * Bean constructor with cms object for path validation.
108: * <p>
109: *
110: * @param cms used to test the working path for valididty.
111: */
112: public CmsTagReplaceSettings(CmsObject cms) {
113:
114: // Treemap guarantees no duplicate keys (ambiguous replacements) and the same default
115: // property ID's for the same replacement strings due to the ordering:
116: m_tags2replacementTags = new TreeMap();
117: m_cms = cms;
118: // all tags are registered for creation
119: m_nodeFactory = new PrototypicalNodeFactory();
120: m_deleteTags = new TreeSet(new Comparator() {
121:
122: public int compare(Object o1, Object o2) {
123:
124: return o1.getClass().getName().compareTo(
125: o2.getClass().getName());
126: }
127: });
128: }
129:
130: /**
131: * Returns the value of the shared {@link #PROPERTY_CONTENTOOLS_TAGREPLACE} to set on resources
132: * that have been processed with these settings.
133: * <p>
134: *
135: * @return the value of the shared {@link #PROPERTY_CONTENTOOLS_TAGREPLACE} to set on resources
136: * that have been processed with these settings.
137: */
138: public String getPropertyValueTagReplaceID() {
139:
140: return m_propertyValueTagReplaceID;
141: }
142:
143: /**
144: * Returns the replacements to perform in form of a comma-separated List of "key=value" tokens.
145: * <p>
146: *
147: * @return the replacements to perform in form of a comma-separated List of "key=value" tokens.
148: */
149: public SortedMap getReplacements() {
150:
151: return m_tags2replacementTags;
152: }
153:
154: /**
155: * Returns the path under which files will be processed recursively.
156: * <p>
157: *
158: * @return the path under which files will be processed recursively.
159: */
160: public String getWorkPath() {
161:
162: return m_workPath;
163: }
164:
165: /**
166: * Sets the value of the shared {@link #PROPERTY_CONTENTOOLS_TAGREPLACE} to set on resources
167: * that have been processed with these settings.
168: * <p>
169: *
170: * @param propertyValueTagreplaceID the value of the shared
171: * {@link #PROPERTY_CONTENTOOLS_TAGREPLACE} to set on resources that have been
172: * processed with these settings.
173: *
174: * @throws CmsIllegalArgumentException if the argument is not valid.
175: */
176: public void setPropertyValueTagReplaceID(
177: String propertyValueTagreplaceID)
178: throws CmsIllegalArgumentException {
179:
180: if (CmsStringUtil
181: .isEmptyOrWhitespaceOnly(propertyValueTagreplaceID)) {
182: m_propertyValueTagReplaceID = getDefaultTagReplaceID();
183: } else {
184: m_propertyValueTagReplaceID = propertyValueTagreplaceID;
185: }
186: }
187:
188: /**
189: * Sets the replacements to perform in form of a comma-separated List of "key=value" tokens.
190: * <p>
191: *
192: * @param replacements the replacements to perform in form of a comma-separated List of
193: * "key=value" tokens.
194: *
195: * @throws CmsIllegalArgumentException if the argument is not valid.
196: */
197: public void setReplacements(SortedMap replacements)
198: throws CmsIllegalArgumentException {
199:
200: Iterator itMappings = replacements.entrySet().iterator();
201: Map.Entry entry;
202: String key, value;
203: while (itMappings.hasNext()) {
204: entry = (Map.Entry) itMappings.next();
205: key = (String) entry.getKey();
206: value = (String) entry.getValue();
207: if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)) {
208: // removal
209: Tag deleteTag;
210: String tagName = (key).toLowerCase().trim();
211: try {
212: Vector attributeList = new Vector(1);
213: Attribute tagNameAttribute = new Attribute();
214: tagNameAttribute.setName(tagName);
215: attributeList.add(tagNameAttribute);
216: deleteTag = m_nodeFactory.createTagNode(null, 0, 0,
217: attributeList);
218: m_deleteTags.add(deleteTag);
219: itMappings.remove();
220: } catch (ParserException e) {
221: CmsMessageContainer container = Messages
222: .get()
223: .container(
224: Messages.GUI_ERR_TAGREPLACE_TAGNAME_INVALID_1,
225: tagName);
226: throw new CmsIllegalArgumentException(container, e);
227: }
228: } else {
229: // nop
230: }
231: m_tags2replacementTags = replacements;
232: }
233: // if setPropertyValueTagReplaceID has been invoked earlier with empty value
234: // due to missing user input:
235: if (CmsStringUtil
236: .isEmptyOrWhitespaceOnly(m_propertyValueTagReplaceID)) {
237: // trigger computation of default ID by empty value:
238: setPropertyValueTagReplaceID(null);
239: }
240: }
241:
242: /**
243: * Sets the path under which files will be processed recursively.
244: * <p>
245: *
246: * @param workPath the path under which files will be processed recursively.
247: *
248: * @throws CmsIllegalArgumentException if the argument is not valid.
249: */
250: public void setWorkPath(String workPath)
251: throws CmsIllegalArgumentException {
252:
253: if (CmsStringUtil.isEmptyOrWhitespaceOnly(workPath)) {
254: throw new CmsIllegalArgumentException(Messages.get()
255: .container(Messages.GUI_ERR_WIDGETVALUE_EMPTY_0));
256: }
257: // test if it is a valid path:
258: if (!m_cms.existsResource(workPath)) {
259: throw new CmsIllegalArgumentException(Messages.get()
260: .container(Messages.GUI_ERR_TAGREPLACE_WORKPATH_1,
261: workPath));
262: }
263: m_workPath = workPath;
264:
265: }
266:
267: /**
268: * Returns the Set<{@link org.htmlparser.Tag}> to delete from transformed output.
269: * <p>
270: *
271: * @return the Set<{@link org.htmlparser.Tag}> to delete from transformed output.
272: */
273: protected Set getDeleteTags() {
274:
275: return m_deleteTags;
276: }
277:
278: /**
279: * Transforms the given Tag into the one it has to become by changing it's name and/or
280: * attributes.
281: * <p>
282: *
283: * @param tag the tag to be transformed.
284: *
285: * @return true if the given tag was modified, false else.
286: *
287: */
288: protected boolean replace(org.htmlparser.Tag tag) {
289:
290: boolean result = false;
291: String tagName = tag.getTagName().trim().toLowerCase();
292: String replacementName = (String) m_tags2replacementTags
293: .get(tagName);
294: if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(replacementName)) {
295: // judge this as a bug: getTagName() returns plain name, setter needs leading '/' for
296: // TODO: when updating htmlparser, verify if this has changed / been fixed
297: // closing tags
298: if (tag.isEndTag()) {
299: replacementName = "/" + replacementName;
300: }
301: tag.setTagName(replacementName);
302: result = true;
303: // clear the attributes too:
304: List attributes = tag.getAttributesEx();
305: Iterator itAttribs = attributes.iterator();
306: // skip the "tagname attribute"....
307: itAttribs.next();
308: Attribute attribute;
309: String attName;
310: while (itAttribs.hasNext()) {
311: attribute = (Attribute) itAttribs.next();
312: attName = attribute.getName();
313: if (CmsStringUtil.isEmptyOrWhitespaceOnly(attName)) {
314: // this is the case for e.g. <h1 >
315: // -> becomes a tag with an attribute for tag name and a null name attribute
316: // (for the whitespace!)
317: } else {
318: if (LOG.isDebugEnabled()) {
319: LOG
320: .debug(Messages
321: .get()
322: .getBundle()
323: .key(
324: Messages.LOG_DEBUG_TAGREPLACE_TAG_REMOVE_ATTRIB_2,
325: attName,
326: tag.getTagName()));
327:
328: }
329: itAttribs.remove();
330: if (LOG.isDebugEnabled()) {
331: LOG
332: .debug(Messages
333: .get()
334: .getBundle()
335: .key(
336: Messages.LOG_DEBUG_TAGREPLACE_TAG_REMOVE_ATTRIB_OK_0));
337: }
338: }
339: }
340: }
341: return result;
342: }
343:
344: /**
345: * Computes the default property value for resources that have to be marked as "processed by
346: * these replacement settings".
347: * <p>
348: *
349: * The default value will be the alphabetically sorted string for replacments or the empty
350: * String if the replacements have not been set before.
351: * <p>
352: *
353: * @return the default property value for resources that have to be marked as "processed by
354: * these replacement settings".
355: */
356: private String getDefaultTagReplaceID() {
357:
358: if (m_tags2replacementTags.size() == 0) {
359: return ""; // to know that no replacements were set before and the ID will still have
360: // to be computed later
361: } else {
362: StringBuffer result = new StringBuffer();
363: Map.Entry entry;
364: Iterator itEntries = m_tags2replacementTags.entrySet()
365: .iterator();
366: while (itEntries.hasNext()) {
367: entry = (Map.Entry) itEntries.next();
368: result.append(entry.getKey()).append('=').append(
369: entry.getValue());
370: if (itEntries.hasNext()) {
371: result.append(',');
372: }
373: }
374: return result.toString();
375:
376: }
377: }
378: }
|