📄 chapter17.html
字号:
caller is mistakenly trying to use this constructor where it isn’t
appropriate.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Once the <B>packMarker</B> is
found, it is stripped off and the directory name (terminated by a
‘<B>#</B>’) and the file name (which goes to the end of the line)
are extracted. In both cases, the old separator character is replaced by the one
that is current to this machine using the <B>String
<A NAME="Index3020"></A><A NAME="Index3021"></A>replace( )</B> method. The
old separator is placed at the beginning of the packed file, and you’ll
see how that is extracted later in the listing.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The rest of the constructor is
quite simple. It reads and concatenates each line to the <B>contents</B> until
the <B>endMarker</B> is found.</FONT><BR></P></DIV>
<A NAME="Heading575"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Accessing and writing the listings</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The next set of methods are simple
accessors: <B>directory( )</B>, <B>filename( )</B> (notice the method
can have the same spelling and capitalization as the field) and
<B>contents( )</B>, and <B>hasFile( )</B> to indicate whether this
object contains a file or not. (The need for this will be seen
later.)</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The final three methods are
concerned with writing this code listing into a file, either a packed file via
<B>writePacked( )</B> or a Java source file via <B>writeFile( )</B>.
All <B>writePacked( )</B> needs is the <B>DataOutputStream,</B> which was
opened elsewhere, and represents the file that’s being written. It puts
the header information on the first line and then calls
<B>writeBytes( )</B> to write <B>contents</B> in a “universal”
format.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When writing the Java source file,
the file must be created. This is done via <B>IO.psOpen( )</B>, handing it
a <A NAME="Index3022"></A><A NAME="Index3023"></A><B>File</B> object that
contains not only the file name but also the path. But the question now is: does
this path exist? The user has the option of placing all the source code
directories into a completely different subdirectory, which might not even
exist. So before each file is written,
<A NAME="Index3024"></A><A NAME="Index3025"></A><A NAME="Index3026"></A><B>File.mkdirs( )</B>
is called with the path that you want to write the file into. This will make the
entire path all at once.</FONT><BR></P></DIV>
<A NAME="Heading576"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
Containing the entire collection of listings</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It’s convenient to organize
the listings as subdirectories while the whole collection is being built in
memory. One reason is another sanity check: as each subdirectory of listings is
created, an additional file is added whose name contains the number of files in
that directory.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>DirMap</B> class produces
this effect and demonstrates the concept of a “multimap.” This is
implemented using a
<A NAME="Index3027"></A><A NAME="Index3028"></A><B>Hashtable</B> whose keys are
the subdirectories being created and whose values are
<A NAME="Index3029"></A><B>Vector</B> objects containing the
<B>SourceCodeFile</B> objects in that particular directory. Thus, instead of
mapping a key to a single value, the “multimap” maps a key to a set
of values via the associated <B>Vector</B>. Although this sounds complex,
it’s remarkably straightforward to implement. You’ll see that most
of the size of the <B>DirMap</B> class is due to the portions that write to
files, not to the “multimap” implementation.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">There are two ways you can make a
<B>DirMap</B>: the default constructor assumes that you want the directories to
branch off of the current one, and the second constructor lets you specify an
alternate absolute path for the starting directory.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>add( )</B> method is
where quite a bit of dense action occurs. First, the <B>directory( )</B> is
extracted from the <B>SourceCodeFile</B> you want to add, and then the
<B>Hashtable</B> is examined to see if it contains that key already. If not, a
new <B>Vector</B> is added to the <B>Hashtable</B> and associated with that key.
At this point, the <B>Vector</B> is there, one way or another, and it is
extracted so the <B>SourceCodeFile</B> can be added. Because
<A NAME="Index3030"></A><A NAME="Index3031"></A><B>Vector</B>s can be easily
combined with <B>Hashtable</B>s like this, the power of both is
amplified.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Writing a packed file involves
opening the file to write (as a
<A NAME="Index3032"></A><A NAME="Index3033"></A><B>DataOutputStream</B> so the
data is universally recoverable) and writing the header information about the
old separator on the first line. Next, an <B>Enumeration</B> of the
<B>Hashtable</B> keys is produced and stepped through to select each directory
and to fetch the <B>Vector</B> associated with that directory so each
<B>SourceCodeFile</B> in that <B>Vector</B> can be written to the packed
file.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Writing the Java source files to
their directories in <B>write( ) </B>is almost identical to
<B>writePackedFile( )</B> since both methods simply call the appropriate
method in <B>SourceCodeFile</B>. Here, however, the root path is passed into
<B>SourceCodeFile.writeFile( )</B> and when all the files have been written
the additional file with the name containing the number of files is also
written.</FONT><BR></P></DIV>
<A NAME="Heading577"></A><FONT FACE = "Verdana"><H4 ALIGN="LEFT">
The main program</H4></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The previously described classes
are used within <B>CodePackager</B>. First you see the usage string that gets
printed whenever the end user invokes the program incorrectly, along with the
<B>usage( )</B> method that calls it and exits the program. All
<B>main( )</B> does is determine whether you want to create a packed file
or extract from one, then it ensures the arguments are correct and calls the
appropriate method.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When a packed file is created,
it’s assumed to be made in the current directory, so the <B>DirMap</B> is
created using the default constructor. After the file is opened each line is
read and examined for particular conditions:</FONT><BR></P></DIV>
<OL>
<LI><FONT FACE="Georgia"> If the line starts with the starting marker for
a source code listing, a new <B>SourceCodeFile</B> object is created. The
constructor reads in the rest of the source listing. The handle that results is
directly added to the <B>DirMap</B>.</FONT><LI><FONT FACE="Georgia"> If
the line starts with the end marker for a source code listing, something has
gone wrong, since end markers should be found only by the <B>SourceCodeFile</B>
constructor.</FONT></OL><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When
extracting a packed file, the extraction can be into the current directory or
into an alternate directory, so the <B>DirMap</B> object is created accordingly.
The file is opened and the first line is read. The old file path separator
information is extracted from this line. Then the input is used to create the
first <B>SourceCodeFile</B> object, which is added to the <B>DirMap</B>. New
<B>SourceCodeFile</B> objects are created and added as long as they contain a
file. (The last one created will simply return when it runs out of input and
then <B>hasFile( )</B> will return
false.)</FONT><A NAME="_Toc375545504"></A><A NAME="_Toc408018812"></A><BR></P></DIV>
<A NAME="Heading578"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Checking capitalization
style<BR><A NAME="Index3034"></A><A NAME="Index3035"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Although the previous example can
come in handy as a guide for some project of your own that involves text
processing, this project will be directly useful because it performs a style
check to make sure that your capitalization conforms to the de-facto Java style.
It opens each <B>.java</B> file in the current directory and extracts all the
class names and identifiers, then shows you if any of them don’t meet the
Java style.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">For the program to operate
correctly, you must first build a class name repository to hold all the class
names in the standard Java library. You do this by moving into all the source
code subdirectories for the standard Java library and running
<B>ClassScanner</B> in each subdirectory. Provide as arguments the name of the
repository file (using the same path and name each time) and the <B>-a</B>
command-line option to indicate that the class names should be added to the
repository.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To use the program to check your
code, run it and hand it the path and name of the repository to use. It will
check all the classes and identifiers in the current directory and tell you
which ones don’t follow the typical Java capitalization
style.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You should be aware that the
program isn’t perfect; there a few times when it will point out what it
thinks is a problem but on looking at the code you’ll see that nothing
needs to be changed. This is a little annoying, but it’s still much easier
than trying to find all these cases by staring at your code.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The explanation immediately follows
the listing:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: ClassScanner.java</font>
<font color=#009900>// Scans all files in directory for classes</font>
<font color=#009900>// and identifiers, to check capitalization.</font>
<font color=#009900>// Assumes properly compiling code listings.</font>
<font color=#009900>// Doesn't do everything right, but is a very</font>
<font color=#009900>// useful aid.</font>
<font color=#0000ff>import</font> java.io.*;
<font color=#0000ff>import</font> java.util.*;
<font color=#0000ff>class</font> MultiStringMap <font color=#0000ff>extends</font> Hashtable {
<font color=#0000ff>public</font> <font color=#0000ff>void</font> add(String key, String value) {
<font color=#0000ff>if</font>(!containsKey(key))
put(key, <font color=#0000ff>new</font> Vector());
((Vector)get(key)).addElement(value);
}
<font color=#0000ff>public</font> Vector getVector(String key) {
<font color=#0000ff>if</font>(!containsKey(key)) {
System.err.println(
<font color=#004488>"ERROR: can't find key: "</font> + key);
System.exit(1);
}
<font color=#0000ff>return</font> (Vector)get(key);
}
<font color=#0000ff>public</font> <font color=#0000ff>void</font> printValues(PrintStream p) {
Enumeration k = keys();
<font color=#0000ff>while</font>(k.hasMoreElements()) {
String oneKey = (String)k.nextElement();
Vector val = getVector(oneKey);
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < val.size(); i++)
p.println((String)val.elementAt(i));
}
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> ClassScanner {
<font color=#0000ff>private</font> File path;
<font color=#0000ff>private</font> String[] fileList;
<font color=#0000ff>private</font> Properties classes = <font color=#0000ff>new</font> Properties();
<font color=#0000ff>private</font> MultiStringMap
classMap = <font color=#0000ff>new</font> MultiStringMap(),
identMap = <font color=#0000ff>new</font> MultiStringMap();
<font color=#0000ff>private</font> StreamTokenizer in;
<font color=#0000ff>public</font> ClassScanner() {
path = <font color=#0000ff>new</font> File(<font color=#004488>"."</font>);
fileList = path.list(<font color=#0000ff>new</font> JavaFilter());
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < fileList.length; i++) {
System.out.println(fileList[i]);
scanListing(fileList[i]);
}
}
<font color=#0000ff>void</font> scanListing(String fname) {
<font color=#0000ff>try</font> {
in = <font color=#0000ff>new</font> StreamTokenizer(
<font color=#0000ff>new</font> BufferedReader(
<font color=#0000ff>new</font> FileReader(fname)));
<font color=#009900>// Doesn't seem to work:</font>
<font color=#009900>// in.slashStarComments(true);</font>
<font color=#009900>// in.slashSlashComments(true);</font>
in.ordinaryChar('/');
in.ordinaryChar('.');
in.wordChars('_', '_');
in.eolIsSignificant(<font color=#0000ff>true</font>);
<font color=#0000ff>while</font>(in.nextToken() !=
StreamTokenizer.TT_EOF) {
<font color=#0000ff>if</font>(in.ttype == '/')
eatComments();
<font color=#0000ff>else</font> <font color=#0000ff>if</font>(in.ttype ==
StreamTokenizer.TT_WORD) {
<font color=#0000ff>if</font>(in.sval.equals(<font color=#004488>"class"</font>) ||
in.sval.equals(<font color=#004488>"interface"</font>)) {
<font color=#009900>// Get class name:</font>
<font color=#0000ff>while</font>(in.nextToken() !=
StreamTokenizer.TT_EOF
&& in.ttype !=
StreamTokenizer.TT_WORD)
;
classes.put(in.sval, in.sval);
classMap.add(fname, in.sval);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -