001/*license*\ 002 Codelet: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) 003 004 This software is dual-licensed under the: 005 - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; 006 - Apache Software License (ASL) version 2.0. 007 008 Either license may be applied at your discretion. More information may be found at 009 - http://en.wikipedia.org/wiki/Multi-licensing. 010 011 The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at: 012 - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt 013 - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt 014\*license*/ 015package com.github.aliteralmind.codelet.type; 016 import java.nio.file.InvalidPathException; 017 import java.nio.file.Path; 018 import java.nio.file.Paths; 019 import com.github.xbn.io.PathMustBe; 020 import com.github.aliteralmind.codelet.CodeletFormatException; 021 import com.github.aliteralmind.codelet.CodeletInstance; 022 import com.github.aliteralmind.codelet.CodeletType; 023 import com.github.aliteralmind.codelet.CustomizationInstructions; 024 import com.github.aliteralmind.codelet.TagletOfTypeProcessor; 025 import com.github.aliteralmind.codelet.TagletTextUtil; 026 import com.github.xbn.io.PlainTextFileUtil; 027 import java.nio.file.AccessDeniedException; 028 import java.nio.file.NoSuchFileException; 029 import java.util.Iterator; 030 import static com.github.aliteralmind.codelet.CodeletBaseConfig.*; 031 import static com.github.xbn.lang.XbnConstants.*; 032/** 033 <p>Reads a {@link com.github.aliteralmind.codelet.CodeletType#FILE_TEXT {@.file.textlet}} taglet and outputs its replacement text.</p> 034 035 * @since 0.1.0 036 * @author Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <a href="http://codelet.aliteralmind.com">{@code http://codelet.aliteralmind.com}</a>, <a href="https://github.com/aliteralmind/codelet">{@code https://github.com/aliteralmind/codelet}</a> 037 **/ 038public class FileTextProcessor extends TagletOfTypeProcessor<FileTextTemplate> { 039 /** 040 <p>Create a new instance from an {@code CodeletInstance}.</p> 041 042 * <p>Equal to <code>{@link com.github.aliteralmind.codelet.TagletOfTypeProcessor#TagletOfTypeProcessor(CodeletType, CodeletInstance) super}(CodeletType.FILE_TEXT, instance)</code> 043 */ 044 public FileTextProcessor(CodeletInstance instance) throws ClassNotFoundException, NoSuchMethodException, NoSuchFileException, AccessDeniedException { 045 super(CodeletType.FILE_TEXT, instance); 046 047 if(getClassOrFilePortion().contains("(")) { 048 throw new CodeletFormatException(instance, "File-text taglets cannot contain command-line parameters"); 049 } 050 051 Iterator<String> fileTextLineItr = getLineIteratorFromCodeletPath(instance); 052 053// String strSig = getStringSigForFileText(); 054// SimpleMethodSignature sig = getCustomizerSigFromString(strSig); 055 056 CustomizationInstructions<FileTextTemplate> instructions = 057 ((getCustomizerPortion() != null) 058 ? getCustomCustomizationInstructions(CodeletType.FILE_TEXT) 059 : newInstructionsForDefaults( 060 new CustomizationInstructions<FileTextTemplate>(instance, CodeletType.FILE_TEXT))); 061 062 crashIfClassOrFileCannotUseCustomizer(instructions); 063 064 String fileTextCustomized = instructions.getCustomizedBody(instance, fileTextLineItr); 065 066 FileTextTemplate template = getTemplateFromInstructionsOverrideOrDefault( 067 instructions); 068 069 String finalOutput = template.fillBody(fileTextCustomized).getRendered(instance); 070 setFullyProcessedOutput(finalOutput); 071 } 072 private final Iterator<String> getLineIteratorFromCodeletPath(CodeletInstance instance) { 073 074 boolean doDebug = isDebugOn(instance, "zzFileTextProcessor.obtainingfile"); 075 076 //See CodeletInstance.FILE_TEXT 077 078 if(doDebug) { 079 debugln("Obtaining line iterator to {@.file.textlet} file..."); 080 debugln(" - Raw file path from {@.file.textlet}: " + getClassOrFilePortion()); 081 debugln(" - TagletTextUtil.getFilePath(instance): " + TagletTextUtil.getFilePath(instance)); 082 } 083 084 String pathStr = getPathStrWithEnvVarPrefix(instance, doDebug); 085 086 PathMustBe pmb = new PathMustBe().existing().readable(); 087 088 /* 089 First assume absolute. This also works if the path is relative to the directory in which javadoc.exe was invoked (and the file separators happen to be correct for the OS). 090 */ 091 Path path = Paths.get(pathStr); 092 if(pmb.isGood(path)) { 093 if(doDebug) { 094 debugln(" SUCCESS: Path is either absolute, or relative to the directory in which javadoc.exe was invoked (note: file-separators not yet changed)."); 095 } 096 097 return PlainTextFileUtil.getLineIterator(path.toString(), "[path to file]"); 098 } 099 100 101 if(doDebug) { 102 debugln(" Replacing all '/' file separators with \"" + FILE_SEP + "\"..."); 103 } 104 if(!FILE_SEP.equals("/")) { 105 if(doDebug) { 106 debugln(" This system's file separator is already '/'. Nothing to change."); 107 } 108 } else { 109 pathStr.replace("/", FILE_SEP); 110 } 111 112 //It's not absolute. 113 /* 114 It's also not relative to the javadoc.exe-invoking directory (the current working directory: cwd) WITH THE ORIGINAL FILE-SEPARATORS. Let's try again now with the new file separators. 115 */ 116 path = Paths.get(pathStr); 117 if(pmb.isGood(path)) { 118 if(doDebug) { 119 debugln(" SUCCESS: Path is relative to the directory in which javadoc.exe was invoked."); 120 } 121 122 return PlainTextFileUtil.getLineIterator(path.toString(), "[path to file]"); 123 } 124 125 //It's not relative to the cwd. It must be relative to the enclosing 126 //file. If it's not, die. 127 128 String parentPath = instance.getEnclosingFile().getParent(); 129 130 if(!parentPath.endsWith(FILE_SEP)) { 131 parentPath += FILE_SEP; 132 } 133 134 if(doDebug) { 135 debugln(" Path to file is not absolute, and not relative to the javadoc.exe-invoking directory. It MUST be relative to the directory of its enclosing file:"); 136 debugln(" - File: " + instance.getEnclosingFile()); 137 debugln(" - Parent: " + parentPath); 138 } 139 140 try { 141 path = Paths.get(parentPath, path.toString()); 142 } catch(InvalidPathException ipx) { 143 throw new InvalidPathException(parentPath + path.toString(), "{@.file.textlet} path is invalid. Not absolute, not relative to javadoc.exe invoking directory, and not relative to enclosing file"); 144 } 145 146 return PlainTextFileUtil.getLineIterator(path.toString(), "[path to {@.file.textlet} file]"); 147 } 148 private final String getPathStrWithEnvVarPrefix(CodeletInstance instance, boolean do_debug) { 149 150 //See com.github.aliteralmind.codelet.CodeletType#FILE_TEXT 151 152 String rawPath = TagletTextUtil.getFilePath(instance); 153 154 if(!rawPath.startsWith("$<")) { 155 return rawPath; 156 } 157 158 int idxCloseCurly = rawPath.indexOf('>'); 159 160 String envVarName = null; 161 try { 162 envVarName = rawPath.substring("$<".length(), idxCloseCurly); 163 } catch(StringIndexOutOfBoundsException sbx) { 164 throw new CodeletFormatException(instance, "File path begins \"$<\", but close sharp ('>') not found.", sbx); 165 } 166 String envVar = System.getenv(envVarName); 167 String sysProp = System.getProperty(envVarName); 168 169 boolean isEnvVar = (envVar != null && envVar.length() > 0); 170 boolean isSysProp = (sysProp != null && sysProp.length() > 0); 171 172 if(isEnvVar) { 173 if(isSysProp) { 174 throw new CodeletFormatException(instance, "File path begins with \"$<" + envVarName + ">\". \"" + envVarName + "\" is both an environment variable and a system property. It must be one or the other." + LINE_SEP + 175 "System.getenv(\"" + envVarName + "\")=\"" + envVar + "\"" + LINE_SEP + 176 "System.getProperty(\"" + envVarName + "\")=\"" + sysProp + "\""); 177 } 178 179 } else if(isSysProp) { 180 envVar = sysProp; 181 } else { 182 throw new CodeletFormatException(instance, "File path begins with \"$<" + envVarName + ">\". The value of both System.getenv(\"" + envVarName + "\") (" + 183 ((envVar == null) ? "null" : "\"\"") + 184 ") and System.getProperty(\"" + envVarName + "\"), (" + 185 ((sysProp == null) ? "null" : "\"\"") + 186 ") are null/empty-string"); 187 } 188 189 if(do_debug) { 190 debugln(" " + 191 (isSysProp ? "System property" : "Environment variable") + 192 " found: Name=" + envVarName + ", value=" + envVar + ""); 193 debugln(" - Raw file path from {@.file.textlet}: " + getClassOrFilePortion()); 194 debugln(" - TagletTextUtil.getFilePath(instance): " + TagletTextUtil.getFilePath(instance)); 195 } 196 197 return envVar + rawPath.substring(idxCloseCurly + 1); 198 } 199 /** 200 <p>Get the string signature for a {@code {@.file.textlet}} taglets only. Except where noted, this does not validate the taglet text or the returned signature.</p> 201 202 <p>This follows the same steps as {@link com.github.aliteralmind.codelet.TagletOfTypeProcessor#getStringSignature() getStringSignature}, except<ul> 203 <li>The customizer portion of the taglet, when provided, must contain either an underscore ({@code '_'}) postfix or the processor's full function name.</li> 204 <li>The {@linkplain com.github.aliteralmind.codelet.CodeletType#getDefaultLineProcNamePrefix() default function name prefix} is {@code "getFileTextConfig_"}</li> 205 </ul></p> 206 207 * @exception CodeletFormatException If no function name or underscore-postfix is provided. 208 */ 209 public String getStringSigForFileText() { 210 String sig = getCustomizerPortion(); 211 212 boolean doDebug = isDebugOn(getInstance(), 213 "zzTagletOfTypeProcessor.getCustomizerSigFromString"); 214 215 if(sig == null) { 216 if(doDebug) { 217 debugln(" No Customizer"); 218 } 219 return null; 220 } 221 222 if(sig.equals("()")) { 223 throw new CodeletFormatException(getInstance(), "Customizer portion for a " + CodeletType.FILE_TEXT.getName() + " taglet is equal to \"()\". The processor's function name or underscore-postfix must be specified."); 224 } 225 226 return getSig2PrnsApnddForNameOrPostfix("", sig, doDebug); 227 } 228}