001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.infra.utils.comment.handlers;
038:
039: import org.netbeans.installer.infra.utils.comment.utils.Utils;
040: import java.io.File;
041: import java.io.IOException;
042: import java.util.regex.Pattern;
043:
044: /**
045: * The base class for handling file types in which line-based comments are used,
046: * such as java-style properties files or shell scripts.
047: *
048: * @author Kirill Sorokin
049: */
050: public abstract class LineFileHandler implements FileHandler {
051: /////////////////////////////////////////////////////////////////////////////////
052: // Instance
053: /**
054: * The regular expression pattern which matches the line which is a comment.
055: */
056: protected final Pattern commentPattern;
057:
058: /**
059: * The regular expression pattern which matches the line which should be
060: * ignored. This is used to skip the non-whitespace lines that can preceed the
061: * initial comment such as <code>#!/bin/sh</code> in shell scripts.
062: */
063: protected final Pattern ignorePattern;
064:
065: /**
066: * The prefix which should be used for each line in the comment. If there should
067: * be no prefix - an empty line should be used (which is very unlikely in this
068: * case).
069: */
070: protected final String commentPrefix;
071:
072: /**
073: * The cached file contents.
074: */
075: protected String contents;
076:
077: // constructor //////////////////////////////////////////////////////////////////
078: /**
079: * The constructor which should be called by the extending classes. It merely
080: * sets the class fields, performnig some basic validation.
081: *
082: * @param commentPattern The regular expression pattern which matches the line
083: * which is a comment.
084: * @param ignorePattern The regular expression pattern which matches the line
085: * which should be ignored.
086: * @param commentPrefix The prefix which should be used for each line in the
087: * comment.
088: * @throws java.lang.IllegalArgumentException if the parameters validation
089: * fails.
090: */
091: protected LineFileHandler(final Pattern commentPattern,
092: final Pattern ignorePattern, final String commentPrefix) {
093: if (commentPattern == null) {
094: throw new IllegalArgumentException(
095: "The 'commentPattern' parameter cannot be null."); // NOI18N
096: }
097: this .commentPattern = commentPattern;
098:
099: if (ignorePattern == null) {
100: throw new IllegalArgumentException(
101: "The 'ignorePattern' parameter cannot be null."); // NOI18N
102: }
103: this .ignorePattern = ignorePattern;
104:
105: if (commentPrefix == null) {
106: throw new IllegalArgumentException(
107: "The 'commentPrefix' parameter cannot be null."); // NOI18N
108: }
109: this .commentPrefix = commentPrefix;
110: }
111:
112: // public ///////////////////////////////////////////////////////////////////////
113: /**
114: * {@inheritDoc}
115: */
116: public final void load(final File file) throws IOException {
117: if (file == null) {
118: throw new IllegalArgumentException(
119: "The 'file' parameter cannot be null."); // NOI18N
120: }
121:
122: contents = Utils.readFile(file);
123: }
124:
125: /**
126: * {@inheritDoc}
127: */
128: public final void save(final File file) throws IOException {
129: if (file == null) {
130: throw new IllegalArgumentException(
131: "The 'file' parameter cannot be null."); // NOI18N
132: }
133: if (contents == null) {
134: throw new IllegalStateException(
135: "The contents cache has not been intialized."); // NOI18N
136: }
137:
138: Utils.writeFile(file, contents);
139: }
140:
141: /**
142: * {@inheritDoc}
143: */
144: public final String getCurrentComment() {
145: if (contents == null) {
146: throw new IllegalStateException(
147: "The contents cache has not been intialized."); // NOI18N
148: }
149:
150: final StringBuilder builder = new StringBuilder();
151: final String[] lines = contents.split(Utils.NL_PATTERN);
152:
153: int i = 0;
154:
155: // skip the leading whitespace and ignored lines
156: for (; i < lines.length; i++) {
157: if (!lines[i].trim().equals("")
158: && !ignorePattern.matcher(lines[i]).matches()) {
159: break;
160: }
161: }
162:
163: // read the comment
164: for (; i < lines.length; i++) {
165: if (commentPattern.matcher(lines[i]).matches()) {
166: builder.append(lines[i]).append(Utils.NL);
167: } else {
168: break;
169: }
170: }
171:
172: return builder.length() > 0 ? builder.toString() : null;
173: }
174:
175: /**
176: * {@inheritDoc}
177: */
178: public final String getCorrectComment(final String text,
179: final int lineLength) {
180: return commentPrefix + Utils.NL
181: + Utils.reformat(text, commentPrefix, lineLength)
182: + commentPrefix + Utils.NL;
183: }
184:
185: /**
186: * {@inheritDoc}
187: */
188: public final void insertComment(final String text,
189: final int lineLength) {
190: if (text == null) {
191: throw new IllegalArgumentException(
192: "The 'text' parameter cannot be null."); // NOI18N
193: }
194: if (lineLength <= 0) {
195: throw new IllegalArgumentException(
196: "The 'lineLength' parameter must be positive."); // NOI18N
197: }
198: if (contents == null) {
199: throw new IllegalStateException(
200: "The contents cache has not been intialized."); // NOI18N
201: }
202:
203: final StringBuilder builder = new StringBuilder();
204: final String[] lines = contents.split(Utils.NL_PATTERN);
205: final String comment = getCorrectComment(text, lineLength);
206:
207: int i = 0;
208:
209: // transfer the existing leading whitespace and ignored lines
210: for (; i < lines.length; i++) {
211: if (!lines[i].trim().equals("")
212: && !ignorePattern.matcher(lines[i]).matches()) {
213: break;
214: } else {
215: builder.append(lines[i]).append(Utils.NL);
216: }
217: }
218:
219: // transfer the comment and an empty line
220: builder.append(comment).append(Utils.NL);
221:
222: // transfer the rest of file
223: for (; i < lines.length; i++) {
224: builder.append(lines[i]).append(Utils.NL);
225: }
226:
227: contents = builder.toString();
228: }
229:
230: /**
231: * {@inheritDoc}
232: */
233: public final void updateComment(final String text,
234: final int lineLength) {
235: if (text == null) {
236: throw new IllegalArgumentException(
237: "The 'text' parameter cannot be null."); // NOI18N
238: }
239: if (lineLength <= 0) {
240: throw new IllegalArgumentException(
241: "The 'lineLength' parameter must be positive."); // NOI18N
242: }
243: if (contents == null) {
244: throw new IllegalStateException(
245: "The contents cache has not been intialized."); // NOI18N
246: }
247:
248: final String currentComment = getCurrentComment();
249:
250: if (currentComment == null) {
251: insertComment(text, lineLength);
252: return;
253: }
254:
255: final String correctComment = getCorrectComment(text,
256: lineLength);
257:
258: // we don't need to update anything if the current initial comment is the
259: // same as the correct one
260: if (currentComment.equals(correctComment)) {
261: return;
262: }
263:
264: final StringBuilder builder = new StringBuilder();
265: final String[] lines = contents.split(Utils.NL_PATTERN);
266:
267: int i = 0;
268:
269: // skip the leading whitespace and the ignored lines
270: for (; i < lines.length; i++) {
271: final String trimmed = lines[i].trim();
272:
273: if (!trimmed.equals("")
274: && !ignorePattern.matcher(lines[i]).matches()) {
275: break;
276: }
277:
278: builder.append(lines[i]).append(Utils.NL);
279: }
280:
281: // skip the comment
282: for (; i < lines.length; i++) {
283: if (!commentPattern.matcher(lines[i]).matches()) {
284: break;
285: }
286: }
287:
288: // skip the empty lines after the comment
289: for (; i < lines.length; i++) {
290: if (!lines[i].trim().equals("")) {
291: break;
292: }
293: }
294:
295: // output the correct comment and an empty line
296: builder.append(correctComment).append(Utils.NL);
297:
298: // transfer the rest of the file
299: for (; i < lines.length; i++) {
300: builder.append(lines[i]).append(Utils.NL);
301: }
302:
303: contents = builder.toString();
304: }
305: }
|