001: // CvsEntryResource.java
002: // $Id: CvsEntryResource.java,v 1.20 2003/02/27 16:32:22 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigedit.cvs;
007:
008: import java.io.ByteArrayInputStream;
009: import java.io.ByteArrayOutputStream;
010: import java.io.InputStream;
011: import java.io.IOException;
012: import java.io.OutputStream;
013: import java.io.StringBufferInputStream;
014:
015: import java.util.Hashtable;
016:
017: import org.w3c.cvs.CvsDirectory;
018: import org.w3c.cvs.CvsException;
019:
020: import org.w3c.www.http.HTTP;
021: import org.w3c.www.http.HttpEntityMessage;
022: import org.w3c.www.http.HttpFactory;
023: import org.w3c.www.http.HttpInteger;
024: import org.w3c.www.http.HttpMessage;
025:
026: import org.w3c.jigsaw.http.HTTPException;
027: import org.w3c.jigsaw.http.Reply;
028: import org.w3c.jigsaw.http.Request;
029:
030: import org.w3c.jigsaw.frames.HTTPFrame;
031: import org.w3c.jigsaw.frames.PostableFrame;
032:
033: import org.w3c.jigsaw.forms.URLDecoder;
034: import org.w3c.jigsaw.forms.URLDecoderException;
035:
036: import org.w3c.tools.resources.AttributeHolder;
037: import org.w3c.tools.resources.ContainerResource;
038: import org.w3c.tools.resources.DummyResourceReference;
039: import org.w3c.tools.resources.FileResource;
040: import org.w3c.tools.resources.FramedResource;
041: import org.w3c.tools.resources.InvalidResourceException;
042: import org.w3c.tools.resources.ProtocolException;
043: import org.w3c.tools.resources.Resource;
044: import org.w3c.tools.resources.ResourceFrame;
045: import org.w3c.tools.resources.ResourceReference;
046:
047: import org.w3c.www.mime.MimeType;
048: import org.w3c.www.mime.Utils;
049:
050: import org.w3c.jigsaw.html.HtmlGenerator;
051:
052: /**
053: * This class exports the state of an entry. For the time being it doesn't
054: * do much, but it should act as a directory (container) for all revisions
055: * of a document.
056: * <p>The lookup method of this entity should be able to turn a revision number
057: * into an editable (or viewable) document.
058: */
059:
060: public class CvsEntryResource extends ContainerResource {
061:
062: protected String name = null;
063: protected ResourceReference rr_cvsframe = null;
064:
065: static byte startAnchor[] = { (byte) '<', (byte) 'a', (byte) ' ',
066: (byte) 'h', (byte) 'r', (byte) 'e', (byte) 'f', (byte) '=',
067: (byte) '"' };
068: static byte midAnchor[] = { (byte) '"', (byte) '>' };
069:
070: static byte endAnchor[] = { (byte) '<', (byte) '/', (byte) 'a',
071: (byte) '>' };
072:
073: static byte edittext[] = { (byte) 'e', (byte) 'd', (byte) 'i',
074: (byte) 't', (byte) ' ', (byte) 'l', (byte) 'o', (byte) 'g' };
075:
076: static byte pattern[] = { (byte) 'r', (byte) 'e', (byte) 'v',
077: (byte) 'i', (byte) 's', (byte) 'i', (byte) 'o', (byte) 'n' };
078:
079: static byte increments[] = new byte[128];
080:
081: static {
082: for (int i = 0; i < 128; i++) {
083: increments[i] = 8;
084: }
085:
086: increments[(int) 'r'] = 7;
087: increments[(int) 'e'] = 6;
088: increments[(int) 'v'] = 5;
089: increments[(int) 'i'] = 2;
090: increments[(int) 's'] = 3;
091: increments[(int) 'o'] = 1;
092: }
093:
094: class RevisionNumberException extends Exception {
095:
096: RevisionNumberException(String message) {
097: super (message);
098: }
099:
100: }
101:
102: class RevisionResource extends FramedResource {
103:
104: ResourceReference rr = null;
105:
106: public ResourceReference getResourceReference() {
107: if (rr == null)
108: rr = new DummyResourceReference(this );
109: return rr;
110: }
111:
112: RevisionResource(String revision) {
113: registerFrame(new RevisionFrame(revision), new Hashtable(3));
114: }
115: }
116:
117: class RevisionFrame extends HTTPFrame {
118:
119: String revision = null;
120:
121: protected synchronized void updateHeaders() {
122: try {
123: CvsFrame cvsframe = (CvsFrame) rr_cvsframe.lock();
124: Resource res = cvsframe.getResource();
125: ResourceReference rr_dir = res.getParent();
126: try {
127: Resource parent = rr_dir.lock();
128: if (parent instanceof ContainerResource) {
129: ContainerResource dir = (ContainerResource) parent;
130: ResourceReference rr_res = dir.lookup(name);
131: if (rr_res == null) {
132: this .setValue(ATTR_CONTENT_TYPE,
133: org.w3c.www.mime.Utils
134: .getMimeType(name));
135: return;
136: }
137: try {
138: res = rr_res.lock();
139: if (res instanceof FileResource) {
140: HTTPFrame httpFrame = (HTTPFrame) res
141: .getFrame(Class
142: .forName("org.w3c.jigsaw.frames.HTTPFrame"));
143: this .setValue(ATTR_CONTENT_LANGUAGE,
144: httpFrame.getContentLanguage());
145: this .setValue(ATTR_CONTENT_ENCODING,
146: httpFrame.getContentEncoding());
147: this .setValue(ATTR_CONTENT_TYPE,
148: httpFrame.getContentType());
149: }
150: } catch (InvalidResourceException ex) {
151: //nothing to do ;(
152: } catch (ClassNotFoundException ex2) {
153: //pfff
154: } finally {
155: rr_res.unlock();
156: }
157: }
158: } catch (InvalidResourceException ex) {
159: //nothing to do ;(
160: } finally {
161: rr_dir.unlock();
162: }
163: } catch (InvalidResourceException ex) {
164: //nothing to do ;(
165: } finally {
166: rr_cvsframe.unlock();
167: }
168: }
169:
170: public Reply createDefaultReply(Request request, int status) {
171: Reply reply = super .createDefaultReply(request, status);
172: HttpInteger contentlength = HttpFactory.makeInteger(getInt(
173: ATTR_CONTENT_LENGTH, -1));
174: reply.setHeaderValue(Reply.H_CONTENT_LENGTH, contentlength);
175: return reply;
176: }
177:
178: public Reply get(Request request) throws ProtocolException {
179: try {
180: try {
181: checkRevisionNumber(revision);
182: } catch (RevisionNumberException ex) {
183: Reply error = request.makeReply(HTTP.BAD_REQUEST);
184: error.setContent("Bad revision number : <b>"
185: + ex.getMessage() + "</b>");
186: return error;
187: }
188: ByteArrayOutputStream out = new ByteArrayOutputStream();
189: try {
190: getCvsManager().revert(name, out, revision, null);
191: byte content[] = out.toByteArray();
192: ByteArrayInputStream in = new ByteArrayInputStream(
193: content);
194: this .setValue(ATTR_CONTENT_LENGTH, new Integer(
195: content.length));
196: Reply reply = createDefaultReply(request, HTTP.OK);
197: reply.setStream(in);
198: // fancy thing, we should get the content location
199: // of the "real" resource...
200: String req_s = request.getURL().toString();
201: int first_sl = req_s.lastIndexOf((int) '/');
202: int second_sl = req_s.lastIndexOf((int) '/',
203: first_sl - 1);
204: int third_sl = req_s.lastIndexOf((int) '/',
205: second_sl - 1);
206: String sub_u = req_s.substring(0, third_sl);
207: reply.setContentLocation(sub_u + '/' + name);
208: return reply;
209: } catch (InvalidResourceException ex) {
210: Reply error = request
211: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
212: error.setContent("CvsFrame invalid");
213: return error;
214: } finally {
215: rr_cvsframe.unlock();
216: }
217: } catch (CvsException ex) {
218: Reply error = request
219: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
220: error.setContent("Cvs operation failed : <b>"
221: + ex.getMessage() + "</b>");
222: return error;
223: }
224: }
225:
226: RevisionFrame(String revision) {
227: this .revision = revision;
228: updateHeaders();
229: }
230:
231: }
232:
233: class CvsEntryFrame extends PostableFrame {
234:
235: protected Reply dolog(Request request) throws ProtocolException {
236: String log = null;
237: try {
238: log = getCvsManager().log(name);
239: } catch (CvsException cvs_ex) {
240: Reply error = request
241: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
242: HtmlGenerator g = getHtmlGenerator("CVS log command failed");
243: g
244: .append("<p>The CVS <strong>log</strong> command failed "
245: + " on "
246: + name
247: + " with the following error message: "
248: + "<em>"
249: + cvs_ex.getMessage()
250: + "</em>"
251: + "<hr> from class: "
252: + this .getClass().getName());
253: error.setStream(g);
254: throw new HTTPException(error);
255: } catch (InvalidResourceException ex) {
256: Reply error = request
257: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
258: HtmlGenerator g = getHtmlGenerator("CVS log command failed");
259: g
260: .append("<p>The CVS <strong>log</strong> command failed "
261: + " on "
262: + name
263: + " with the following error message: "
264: + "<em>"
265: + ex.getMessage()
266: + "</em>"
267: + "<hr> from class: "
268: + this .getClass().getName());
269: error.setStream(g);
270: throw new HTTPException(error);
271: } finally {
272: rr_cvsframe.unlock();
273: }
274: Reply reply = request.makeReply(HTTP.OK);
275: reply.setContentType(MimeType.TEXT_HTML);
276: reply.setStream(parseLog(log));
277: return reply;
278: }
279:
280: protected Reply dodiff(Request request) throws HTTPException {
281: String diff = null;
282: try {
283: diff = getCvsManager().diff(name);
284: } catch (CvsException cvs_ex) {
285: Reply error = request
286: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
287: HtmlGenerator g = getHtmlGenerator("CVS diff command failed");
288: g
289: .append("<p>The CVS <strong>diff</strong> command failed "
290: + " on "
291: + name
292: + " with the following error message: "
293: + "<em>"
294: + cvs_ex.getMessage()
295: + "</em>"
296: + "<hr> from class: "
297: + this .getClass().getName());
298: error.setStream(g);
299: throw new HTTPException(error);
300: } catch (InvalidResourceException ex) {
301: Reply error = request
302: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
303: HtmlGenerator g = getHtmlGenerator("CVS diff command failed");
304: g
305: .append("<p>The CVS <strong>diff</strong> command failed "
306: + " on "
307: + name
308: + " with the following error message: "
309: + "<em>"
310: + ex.getMessage()
311: + "</em>"
312: + "<hr> from class: "
313: + this .getClass().getName());
314: error.setStream(g);
315: throw new HTTPException(error);
316: } finally {
317: rr_cvsframe.unlock();
318: }
319: // if there are no differences, generate a dummy report
320: Reply reply = request.makeReply(HTTP.OK);
321: if ((diff == null) || (diff.length() == 0)) {
322: HtmlGenerator g = getHtmlGenerator("CVS diff command results");
323: g.append("<P>No differences between " + name
324: + " and the repository</P>");
325: reply.setStream(g);
326: } else {
327: HtmlGenerator g = getHtmlGenerator("Diff result");
328: g.append("<center>");
329: g
330: .append(" [ <a href=\"../\">Up to directory</A> ] ·");
331: g.append(" [ <a href=\"" + getCvsURL(),
332: "\">Back to CVS</A> ] \n");
333: g.append("</center>");
334: g.append("<hr noshade width=\"80%\"><p>");
335: g.append("<span class=\"title\"> CVS diff of ", name,
336: "</span>\n");
337: g.append("<pre>", parseDiff(diff), "</pre>\n");
338: g.append("<p><hr noshade width=\"80%\">");
339: g.append("<center>");
340: g
341: .append(" [ <a href=\"../\">Up to directory</A> ] ·");
342: g.append(" [ <a href=\"" + getCvsURL(),
343: "\">Back to CVS</A> ] \n");
344: g.append("</center>");
345: reply.setStream(g);
346: }
347: return reply;
348: }
349:
350: protected Reply doEditRev(Request request, String revision)
351: throws HTTPException {
352: HtmlGenerator g = getHtmlGenerator("Edit log");
353: g.append("<center>");
354: g
355: .append(" [ <a href=\"../\">Up to directory</A> ] · ");
356: g.append(" [ <a href=\"" + getCvsURL()
357: + "\">Back to CVS</A> ] · ");
358: g.append(" [ <a href=\"./", name
359: + "?log\">Back to log</A> ] ");
360: g.append("<hr noshade width=\"80%\"><p>");
361: g.append("<span class=\"title\"> Edit log comment of ",
362: name, " (revision " + revision + ")</span>\n");
363: g.append("<form method=\"post\">\n");
364: g
365: .append(
366: "<input type=\"hidden\" name=\"revision\" value=\"",
367: revision, "\">\n");
368: g.append("<table border=\"0\">\n");
369: g.append("<tr><td align=\"left\">");
370: g
371: .append("<textarea name=\"comment\" rows=\"3\" cols=\"50\">\n");
372: g.append("</textarea></td></tr>\n");
373: g.append("</td></tr><tr><td align=\"center\">");
374: g.append("<input type=\"submit\" name=\"submit\" "
375: + "value=\" Save Comment \">");
376: g.append("</td></tr>");
377: g.append("</table></center></form>\n");
378: g.append("<hr noshade width=\"80%\">");
379: Reply reply = request.makeReply(HTTP.OK);
380: reply.setStream(g);
381: return reply;
382: }
383:
384: protected String getRevisionToEdit(Request request)
385: throws HTTPException {
386: String query = request.getQueryString();
387: if (query == null)
388: return null;
389: InputStream in = new StringBufferInputStream(query);
390: URLDecoder d = new URLDecoder(in, getOverrideFlag());
391: try {
392: d.parse();
393: } catch (URLDecoderException e) {
394: Reply error = request.makeReply(HTTP.BAD_REQUEST);
395: error.setContent("Invalid request: "
396: + "unable to decode form data.");
397: throw new HTTPException(error);
398: } catch (IOException e) {
399: Reply error = request.makeReply(HTTP.BAD_REQUEST);
400: error
401: .setContent("Invalid request: unable to read form data.");
402: throw new HTTPException(error);
403: }
404: return d.getValue("editlog");
405: }
406:
407: /**
408: * Getting an entry entity start with dumping the log for this entity
409: * which (should) act as a directory for all versions of it.
410: */
411:
412: public Reply get(Request request) throws ProtocolException {
413: if (!request.hasState("query")) {
414: Reply error = request
415: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
416: error.setContent("Invalid query field.");
417: throw new HTTPException(error);
418: }
419: String cmd = request.getQueryString();
420: if (cmd.equalsIgnoreCase("log")) {
421: return dolog(request);
422: } else if (cmd.equalsIgnoreCase("diff")) {
423: return dodiff(request);
424: } else {
425: String rev = getRevisionToEdit(request);
426: if (rev != null)
427: return doEditRev(request, rev);
428: else {
429: Reply error = request
430: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
431: error.setContent("Unknown command: " + cmd);
432: throw new HTTPException(error);
433: }
434: }
435: }
436:
437: public Reply handle(Request request, URLDecoder data)
438: throws ProtocolException {
439: String revision = data.getValue("revision");
440: String comment = data.getValue("comment");
441:
442: if (revision == null) {
443: Reply error = request
444: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
445: error.setContent("No revision selected !");
446: throw new HTTPException(error);
447: } else if (comment == null) {
448: Reply error = request.makeReply(HTTP.BAD_REQUEST);
449: error.setContent("Empty comment not allowed.");
450: throw new HTTPException(error);
451: }
452: try {
453: String command[] = new String[2];
454: command[0] = "-m" + revision + ":\"" + comment + "\"";
455: command[1] = name;
456: getCvsManager().admin(command);
457: } catch (CvsException ex) {
458: Reply error = request
459: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
460: HtmlGenerator g = getHtmlGenerator("CVS admin command failed");
461: g
462: .append("<p>The CVS <strong>admin</strong> command failed "
463: + " on "
464: + name
465: + " with the following error message: "
466: + "<em>" + ex.getMessage() + "</em>");
467: error.setStream(g);
468: throw new HTTPException(error);
469: } catch (InvalidResourceException inv_ex) {
470: Reply error = request
471: .makeReply(HTTP.INTERNAL_SERVER_ERROR);
472: error.setContent("CvsFrame invalid");
473: return error;
474: }
475: //well done
476: return dolog(request);
477: }
478: }
479:
480: //
481: //CvsEntryResource
482: //
483:
484: private DummyResourceReference self = null;
485: private Hashtable revisions = null;
486: private CvsDirectory cvs = null;
487:
488: protected int getMinor(String revision) {
489: int index = revision.indexOf(".");
490: if (index == -1)
491: return -1;
492: try {
493: return Integer.parseInt(revision.substring(index + 1));
494: } catch (NumberFormatException ex) {
495: return -1;
496: }
497: }
498:
499: protected CvsDirectory getCvsManager()
500: throws InvalidResourceException {
501: if (cvs == null) {
502: try {
503: CvsFrame cvsframe = (CvsFrame) rr_cvsframe.lock();
504: cvs = cvsframe.getCvsManager();
505: } finally {
506: rr_cvsframe.unlock();
507: }
508: }
509: return cvs;
510: }
511:
512: protected String getCvsURL() {
513: String CVSURL = null;
514: try {
515: CvsFrame cvsframe = (CvsFrame) rr_cvsframe.lock();
516: CVSURL = cvsframe.getResource().getURLPath();
517: } catch (InvalidResourceException ex) {
518: return null;
519: } finally {
520: rr_cvsframe.unlock();
521: }
522: return CVSURL;
523: }
524:
525: protected void checkRevisionNumber(String rev)
526: throws RevisionNumberException, CvsException {
527: try {
528: String revision = getCvsManager().revision(name);
529: if (revision == null)
530: return; //can't be tested
531: int minor = getMinor(revision);
532: int minor2 = getMinor(rev);
533: if ((minor2 > minor) || (minor2 <= 0))
534: throw new RevisionNumberException(rev);
535: } catch (InvalidResourceException inv_ex) {
536: throw new RevisionNumberException(
537: "Unable to check revision "
538: + "number, CvsFrame invalid.");
539: } finally {
540: rr_cvsframe.unlock();
541: }
542: }
543:
544: /**
545: * Analogous to standard C's <code>strncmp</code>, for byte arrays.
546: * (Should be in some utility package, I'll put it here for now)
547: * @param ba1 the first byte array
548: * @param off1 where to start in the first array
549: * @param ba2 the second byte array
550: * @param off2 where to start in the second array
551: * @param n the length to compare up to
552: * @return <strong>true</strong> if both specified parts of the
553: * arrays are equal, <strong>false</strong> if they aren't .
554: */
555: static final boolean byteArrayNEquals(byte[] ba1, int off1,
556: byte[] ba2, int off2, int n) {
557: // So that only one addition is needed inside loop
558: int corr = off2 - off1;
559: int max = n + off1;
560: for (int i = off1; i < max; i++)
561: if (ba1[i] != ba2[i + corr])
562: return false;
563: return true;
564: }
565:
566: static final boolean isDigitOrPoint(byte ch) {
567: return ch == '.' || ch == '0' || ch == '1' || ch == '2'
568: || ch == '3' || ch == '4' || ch == '5' || ch == '6'
569: || ch == '7' || ch == '8' || ch == '9';
570: }
571:
572: /**
573: * Does the same as Character.isSpace, without need to cast the
574: * byte into a char.
575: * @param ch the character
576: * @return whether or not ch is ASCII white space
577: * @see java.lang.Character#isSpace
578: */
579: static final boolean isSpace(byte ch) {
580: return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
581: }
582:
583: protected void writeLinks(OutputStream out, byte b[], int offset,
584: int length) throws IOException {
585: out.write(startAnchor);
586: out.write(("./" + name + "/").getBytes());
587: out.write(b, offset, length);
588: out.write(midAnchor);
589: out.write(b, offset, length);
590: out.write(endAnchor);
591: out.write((byte) ' ');
592: out.write((byte) '[');
593: out.write(startAnchor);
594: out.write(("./" + name + "?editlog=").getBytes());
595: out.write(b, offset, length);
596: out.write(midAnchor);
597: out.write(edittext);
598: out.write(endAnchor);
599: out.write((byte) ']');
600: }
601:
602: /**
603: * replace < by <.
604: */
605: protected String parseDiff(String diff) {
606: StringBuffer newdiff = new StringBuffer();
607: int idx = diff.indexOf('<');
608: if (idx == -1)
609: return diff;
610: while ((idx = diff.indexOf('<')) != -1) {
611: newdiff.append(diff.substring(0, idx));
612: newdiff.append("<");
613: diff = diff.substring(idx + 1);
614: }
615: newdiff.append(diff);
616: return newdiff.toString();
617: }
618:
619: protected HtmlGenerator parseLog(String log) {
620: HtmlGenerator g = getHtmlGenerator("CVS log of " + name);
621: String CVSURL = getCvsURL();
622: String head = "[ <a href=\"./../\">Up to directory</a> ] · "
623: + "[ <a href=\"" + CVSURL + "\">Back to CVS</a> ]";
624:
625: g.append("<center>", head,
626: "<hr noshade width=\"80%\"></center><p>");
627: g.append("<span class=\"title\"> CVS log of ", name,
628: "</span>\n");
629: g.append("<div class=\"box\"><pre>\n");
630: //parse the log string
631: ByteArrayOutputStream out = new ByteArrayOutputStream();
632: byte unparsed[] = log.getBytes();
633: int byteIdx = 0;
634: int startIdx = 0;
635: byte ch;
636: byteIdx += 7;
637: do {
638: while (byteIdx < unparsed.length) {
639: if ((ch = unparsed[byteIdx]) == (byte) 'n') {
640: if (byteArrayNEquals(unparsed, byteIdx - 7,
641: pattern, 0, 7)) {
642: break;
643: }
644: }
645: byteIdx += increments[ch >= 0 ? ch : 0];
646: }
647: if (++byteIdx >= unparsed.length)
648: break;
649: //we just found 'revision'
650: while ((byteIdx <= unparsed.length)
651: && isSpace(unparsed[byteIdx])) {
652: byteIdx++;
653: }
654: out.write(unparsed, startIdx, byteIdx - startIdx);
655: startIdx = byteIdx;
656: //get the revision number
657: while ((byteIdx <= unparsed.length)
658: && isDigitOrPoint(unparsed[byteIdx])) {
659: byteIdx++;
660: }
661: //revision number startIdx, byteIdx-1;
662: if (byteIdx - 1 > startIdx) {
663: String rev = new String(unparsed, startIdx, byteIdx
664: - startIdx);
665: try {
666: checkRevisionNumber(rev);
667: writeLinks(out, unparsed, startIdx, byteIdx
668: - startIdx);
669: } catch (Exception ex) {
670: out.write(unparsed, startIdx, byteIdx - startIdx);
671: }
672: }
673: startIdx = byteIdx;
674: } while (byteIdx < unparsed.length);
675: // Add the last chunk of unparsed text
676: int length = unparsed.length - startIdx - 1;
677: if (length > 0)
678: out.write(unparsed, startIdx, length);
679: String parsedlog = out.toString();
680: g.append(parsedlog);
681: //end of parsing
682: g.append("\n</pre></div>\n");
683: g.append("<hr noshade width=\"80%\"><center>", head,
684: "</center><p>");
685: return g;
686: }
687:
688: protected synchronized ResourceReference getRevisionResource(
689: String revision) {
690: RevisionResource res = (RevisionResource) revisions
691: .get(revision);
692: if (res == null) {
693: res = new RevisionResource(revision);
694: revisions.put(revision, res);
695: }
696: return res.getResourceReference();
697: }
698:
699: public ResourceReference lookup(String revision) {
700: try {
701: checkRevisionNumber(revision);
702: return getRevisionResource(revision);
703: } catch (CvsException ex) {
704: } catch (RevisionNumberException ex2) {
705: }
706: return null;
707: }
708:
709: public synchronized ResourceReference getResourceReference() {
710: if (self == null)
711: self = new DummyResourceReference(this );
712: return self;
713: }
714:
715: protected HtmlGenerator getHtmlGenerator(String title) {
716: try {
717: CvsFrame cvsframe = (CvsFrame) rr_cvsframe.lock();
718: return CvsFrame.getHtmlGenerator(cvsframe, title);
719: } catch (InvalidResourceException ex) {
720: return CvsFrame.getHtmlGenerator(title);
721: } finally {
722: rr_cvsframe.unlock();
723: }
724: }
725:
726: CvsEntryResource(ResourceReference rr_cvsframe, String name) {
727: revisions = new Hashtable(3);
728: this .name = name;
729: this .rr_cvsframe = rr_cvsframe;
730: Hashtable defs = new Hashtable(3);
731: registerFrame(new CvsEntryFrame(), defs);
732: }
733:
734: }
|